update jdt.core to I20211025-1800
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/CharDeduplicationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/CharDeduplicationTest.java
new file mode 100644
index 0000000..92fa0a2
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/CharDeduplicationTest.java
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright (c) 2021 jkubitz and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     jkubitz - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.core.tests.compiler;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+
+import org.eclipse.jdt.core.tests.junit.extension.TestCase;
+import org.eclipse.jdt.internal.compiler.util.CharDeduplication;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CharDeduplicationTest extends TestCase {
+
+	public CharDeduplicationTest(String testName) {
+		super(testName);
+	}
+
+	public static Test suite() {
+
+		TestSuite suite = new TestSuite(CharDeduplicationTest.class.getPackageName());
+		suite.addTest(new TestSuite(CharDeduplicationTest.class));
+		return suite;
+	}
+
+	public void testDeduplication() {
+		for (int i = 0; i < 3; i++) {
+			assertDeduplication("a");
+			// ..
+			assertDeduplication("z");
+
+			assertDeduplication("12");
+			assertDeduplication("123");
+			assertDeduplication("1234");
+			assertDeduplication("12345");
+			assertDeduplication("123456");
+			assertNoDeduplication("1234567");
+
+			// new:
+			assertDeduplication("0"); // illegal identifier - but who cares.
+			assertDeduplication("A"); // why not?
+			assertDeduplication("$"); // legal
+			assertDeduplication("_"); // note that "_" may become more common after JEP 302 as keyword for unused
+										// lambda parameter!
+			assertDeduplication("" + (char) 0);
+			// ..
+			assertDeduplication("" + (char) 127);
+			assertNoDeduplication("" + (char) 128); // non-Ascii
+		}
+	}
+
+	public void testDeduplicationMid() {
+		String text = "abcdefghijklmn";
+		for (int start = 0; start < text.length(); start++) {
+			for (int end = start; end < text.length(); end++) {
+				assertDedup(text, (end - start) <= CharDeduplication.OPTIMIZED_LENGTH, start, end);
+			}
+		}
+	}
+
+	@SuppressWarnings("deprecation")
+	public void testDeduplicationTableSize() {
+		CharDeduplication deduplication = CharDeduplication.getThreadLocalInstance();
+		deduplication.reset();// to test the overflow we need to start empty
+		for (int overload = 0; overload < 3; overload++) {
+			HashMap<Integer, char[]> expecteds = new HashMap<>();
+			for (int i = 0; i < CharDeduplication.TABLE_SIZE + overload; i++) {
+				int numberWithFixedLength = 10000 + i;
+				String string = "" + numberWithFixedLength;
+				char[] a = string.toCharArray();
+				char[] expected = deduplication.sharedCopyOfRange(a, 0, a.length);
+				expecteds.put(i, expected);
+			}
+			for (int t = 0; t < 2; t++) {
+				for (int i = 0; i < expecteds.size(); i++) {
+					char[] expected = expecteds.get(i);
+					char[] other = String.valueOf(expected).toCharArray();
+					if (overload == 0 || i > 0) {
+						assertDedup(true, 0, expected.length, expected, other);
+					} else {
+						// situation after table overflow:
+						char[] actual = deduplication.sharedCopyOfRange(other, 0, expected.length);
+						// both actual == expected or actual != expected may happen
+						// but we can assert that the next deduplication works again:
+						char[] other2 = String.valueOf(expected).toCharArray();
+						assertDedup(true, 0, expected.length, actual, other2);
+					}
+				}
+			}
+		}
+	}
+
+	public static void main(String[] args) {
+		CharDeduplicationTest test=new CharDeduplicationTest("");
+		System.out.println("min= ~"+ LongStream.range(0, 100).map(t->test.runPerformanceTest()).min());
+		// min= ~0.36sec
+	}
+
+	public long runPerformanceTest() {
+		long nanoTime = System.nanoTime();
+		CharDeduplication deduplication = CharDeduplication.getThreadLocalInstance();
+		for (int j = 0; j < 100; j++) {
+			for (int i = 0; i < 100_000; i++) {
+				String hexString = Integer.toHexString(i);
+				char[] chars = hexString.toCharArray();
+				deduplication.sharedCopyOfRange(chars, 0, chars.length);
+			}
+		}
+		long nanoTime2 = System.nanoTime();
+		long durationNanos = nanoTime2 - nanoTime;
+		System.out.println(durationNanos);
+		return durationNanos;
+	}
+
+	public void testAll() {
+		testDeduplication();
+		testDeduplicationMid();
+		testDeduplicationTableSize();
+	}
+
+	public void testMultithreaded() throws Exception {
+		int nThreads = 8;
+		List<FutureTask<Object>> tasks = IntStream.range(0, nThreads * 2).mapToObj(i -> new FutureTask<Object>(() -> {
+			testAll();
+			return null;
+		})).collect(Collectors.toList());
+		ExecutorService executor = Executors.newFixedThreadPool(nThreads);
+		tasks.forEach(executor::submit);
+		for (FutureTask<Object> task : tasks) {
+			try {
+				task.get();
+			} catch (Exception e) {
+				throw new AssertionError(e);
+			}
+		}
+		executor.shutdownNow();
+	}
+
+	private void assertDeduplication(String string) {
+		assertDedup(string, true, 0, string.length());
+	}
+
+	private void assertNoDeduplication(String string) {
+		assertDedup(string, false, 0, string.length());
+	}
+
+	private void assertDedup(String string, boolean same, int startPosition, int end) {
+		char[] a = string.toCharArray();
+		char[] b = string.toCharArray();
+		assertNotSame(a, b);
+		CharDeduplication deduplication = CharDeduplication.getThreadLocalInstance();
+		char[] expected = deduplication.sharedCopyOfRange(a, startPosition, end);
+		assertDedup(same, startPosition, end, expected, b);
+	}
+
+	private char[] assertDedup(boolean same, int startPosition, int end, char[] expected, char[] other) {
+		assertNotSame(expected, other);
+		CharDeduplication deduplication = CharDeduplication.getThreadLocalInstance();
+		char[] actual = deduplication.sharedCopyOfRange(other, startPosition, end);
+		String state = "expected=" + String.valueOf(expected) + ", actual=" + String.valueOf(actual);
+		if (same) {
+			assertSame(state, expected, actual);
+		} else {
+			assertNotSame("Expected different instances. But thats not a requirement but an implementation detail test:"
+					+ state, expected, actual);
+		}
+		return actual;
+	}
+}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AllocationExpressionCompletionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AllocationExpressionCompletionTest.java
index 847f772..3069443 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AllocationExpressionCompletionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AllocationExpressionCompletionTest.java
@@ -28,7 +28,40 @@
 /*
  * Completion inside an if statement.
  */
-public void testInIfStatement() {
+public void testInIfStatement1() {
+	this.runTestCheckMethodParse(
+		// compilationUnit:
+		"class Bar {								\n" +
+		"	void foo() {							\n" +
+		"		if (true) {							\n" +
+		"			new z.y.X(1, 2, i);				\n" +
+		"		}									\n" +
+		"	}										\n" +
+		"}\n",
+		// completeBehind:
+		"X(",
+		// expectedCompletionNodeToString:
+		"<CompleteOnAllocationExpression:new z.y.X(<CompleteOnName:>, 2, i)>",
+		// expectedUnitDisplayString:
+		"class Bar {\n" +
+		"  Bar() {\n" +
+		"  }\n" +
+		"  void foo() {\n" +
+		"    if (true)\n" +
+		"        {\n" +
+		"          <CompleteOnAllocationExpression:new z.y.X(<CompleteOnName:>, 2, i)>;\n" +
+		"        }\n" +
+		"  }\n" +
+		"}\n",
+		// expectedCompletionIdentifier:
+		"",
+		// expectedReplacedSource:
+		"new z.y.X(1, 2, i)",
+		// test name
+		"<complete inside an if statement>"
+	);
+}
+public void testInIfStatement2() {
 	this.runTestCheckMethodParse(
 		// compilationUnit:
 		"class Bar {								\n" +
@@ -41,15 +74,16 @@
 		// completeBehind:
 		"X(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnAllocationExpression:new z.y.X(1, 2)>",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  Bar() {\n" +
 		"  }\n" +
 		"  void foo() {\n" +
-		"    {\n" +
-		"      <CompleteOnAllocationExpression:new z.y.X(1, 2)>;\n" +
-		"    }\n" +
+		"    if (true)\n" +
+		"        {\n" +
+		"          new z.y.X(1, 2, <CompleteOnName:>, i);\n" +
+		"        }\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
@@ -66,7 +100,35 @@
  * ie. ClassInstanceCreationExpression ::= 'new' ClassType '(' ArgumentListopt ')' ClassBodyopt
  *		where ClassType is a qualified type name
  */
-public void testNoQualificationQualifiedTypeName() {
+public void testNoQualificationQualifiedTypeName1() {
+	this.runTestCheckMethodParse(
+		// compilationUnit:
+		"class Bar {								\n" +
+		"	void foo() {							\n" +
+		"		new z.y.X(1, 2, i);					\n" +
+		"	}										\n" +
+		"}\n",
+		// completeBehind:
+		"X(",
+		// expectedCompletionNodeToString:
+		"<CompleteOnAllocationExpression:new z.y.X()>",
+		// expectedUnitDisplayString:
+		"class Bar {\n" +
+		"  Bar() {\n" +
+		"  }\n" +
+		"  void foo() {\n" +
+		"    <CompleteOnAllocationExpression:new z.y.X()>;\n" +
+		"  }\n" +
+		"}\n",
+		// expectedCompletionIdentifier:
+		"",
+		// expectedReplacedSource:
+		"",
+		// test name
+		"<complete on non qualified instance creation with qualified type name>"
+	);
+}
+public void testNoQualificationQualifiedTypeName2() {
 	this.runTestCheckMethodParse(
 		// compilationUnit:
 		"class Bar {								\n" +
@@ -77,13 +139,13 @@
 		// completeBehind:
 		"X(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnAllocationExpression:new z.y.X(1, 2)>",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  Bar() {\n" +
 		"  }\n" +
 		"  void foo() {\n" +
-		"    <CompleteOnAllocationExpression:new z.y.X(1, 2)>;\n" +
+		"    new z.y.X(1, 2, <CompleteOnName:>, i);\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
@@ -100,7 +162,35 @@
  * ie. ClassInstanceCreationExpression ::= 'new' ClassType '(' ArgumentListopt ')' ClassBodyopt
  *		where ClassType is a simple type name
  */
-public void testNoQualificationSimpleTypeName() {
+public void testNoQualificationSimpleTypeName1() {
+	this.runTestCheckMethodParse(
+		// compilationUnit:
+		"class Bar {								\n" +
+		"	void foo() {							\n" +
+		"		new X(1, 2, i);						\n" +
+		"	}										\n" +
+		"}\n",
+		// completeBehind:
+		"X(",
+		// expectedCompletionNodeToString:
+		"<CompleteOnAllocationExpression:new X()>",
+		// expectedUnitDisplayString:
+		"class Bar {\n" +
+		"  Bar() {\n" +
+		"  }\n" +
+		"  void foo() {\n" +
+		"    <CompleteOnAllocationExpression:new X()>;\n" +
+		"  }\n" +
+		"}\n",
+		// expectedCompletionIdentifier:
+		"",
+		// expectedReplacedSource:
+		"",
+		// test name
+		"<complete on non qualified instance creation with simple type name>"
+	);
+}
+public void testNoQualificationSimpleTypeName2() {
 	this.runTestCheckMethodParse(
 		// compilationUnit:
 		"class Bar {								\n" +
@@ -111,13 +201,13 @@
 		// completeBehind:
 		"X(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnAllocationExpression:new X(1, 2)>",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  Bar() {\n" +
 		"  }\n" +
 		"  void foo() {\n" +
-		"    <CompleteOnAllocationExpression:new X(1, 2)>;\n" +
+		"    new X(1, 2, <CompleteOnName:>, i);\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
@@ -133,7 +223,35 @@
  *
  * ie. ClassInstanceCreationExpression ::= ClassInstanceCreationExpressionName 'new' SimpleName '(' ArgumentListopt ')' ClassBodyopt
  */
-public void testQualifiedWithName() {
+public void testQualifiedWithName1() {
+	this.runTestCheckMethodParse(
+		// compilationUnit:
+		"class Bar {\n" +
+		"	void foo() {							\n" +
+		"		Buz.x.new X(1, 2, i);				\n" +
+		"	}										\n" +
+		"}\n",
+		// completeBehind:
+		"X(",
+		// expectedCompletionNodeToString:
+		"<CompleteOnQualifiedAllocationExpression:Buz.x.new X(<CompleteOnName:>, 2, i)>",
+		// expectedUnitDisplayString:
+		"class Bar {\n" +
+		"  Bar() {\n" +
+		"  }\n" +
+		"  void foo() {\n" +
+		"    <CompleteOnQualifiedAllocationExpression:Buz.x.new X(<CompleteOnName:>, 2, i)>;\n" +
+		"  }\n" +
+		"}\n",
+		// expectedCompletionIdentifier:
+		"",
+		// expectedReplacedSource:
+		"Buz.x.new X(1, 2, i)",
+		// test name
+		"<complete on name qualified instance creation>"
+	);
+}
+public void testQualifiedWithName2() {
 	this.runTestCheckMethodParse(
 		// compilationUnit:
 		"class Bar {\n" +
@@ -144,13 +262,13 @@
 		// completeBehind:
 		"X(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnQualifiedAllocationExpression:Buz.x.new X(1, 2)>",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  Bar() {\n" +
 		"  }\n" +
 		"  void foo() {\n" +
-		"    <CompleteOnQualifiedAllocationExpression:Buz.x.new X(1, 2)>;\n" +
+		"    Buz.x.new X(1, 2, <CompleteOnName:>, i);\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
@@ -166,7 +284,35 @@
  *
  * ie. ClassInstanceCreationExpression ::= Primary '.' 'new' SimpleName '(' ArgumentListopt ')' ClassBodyopt
  */
-public void testQualifiedWithPrimary() {
+public void testQualifiedWithPrimary1() {
+	this.runTestCheckMethodParse(
+		// compilationUnit:
+		"class Bar {								\n" +
+		"	void foo() {							\n" +
+		"		primary().new X(1, 2, i);			\n" +
+		"	}										\n" +
+		"}\n",
+		// completeBehind:
+		"X(",
+		// expectedCompletionNodeToString:
+		"<CompleteOnQualifiedAllocationExpression:primary().new X(<CompleteOnName:>, 2, i)>",
+		// expectedUnitDisplayString:
+		"class Bar {\n" +
+		"  Bar() {\n" +
+		"  }\n" +
+		"  void foo() {\n" +
+		"    <CompleteOnQualifiedAllocationExpression:primary().new X(<CompleteOnName:>, 2, i)>;\n" +
+		"  }\n" +
+		"}\n",
+		// expectedCompletionIdentifier:
+		"",
+		// expectedReplacedSource:
+		"primary().new X(1, 2, i)",
+		// test name
+		"<complete on primary qualified instance creation>"
+	);
+}
+public void testQualifiedWithPrimary2() {
 	this.runTestCheckMethodParse(
 		// compilationUnit:
 		"class Bar {								\n" +
@@ -177,13 +323,13 @@
 		// completeBehind:
 		"X(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnQualifiedAllocationExpression:primary().new X(1, 2)>",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  Bar() {\n" +
 		"  }\n" +
 		"  void foo() {\n" +
-		"    <CompleteOnQualifiedAllocationExpression:primary().new X(1, 2)>;\n" +
+		"    primary().new X(1, 2, <CompleteOnName:>, i);\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ExplicitConstructorInvocationCompletionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ExplicitConstructorInvocationCompletionTest.java
index c0a0849..2d89e67 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ExplicitConstructorInvocationCompletionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ExplicitConstructorInvocationCompletionTest.java
@@ -49,7 +49,7 @@
 		// completeBehind:
 		"super(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnExplicitConstructorCall:primary().super(1, 2)>;",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  public class InnerBar {\n" +
@@ -58,7 +58,7 @@
 		"  }\n" +
 		"  public class SubInnerBar extends InnerBar {\n" +
 		"    SubInnerBar(Bar x) {\n" +
-		"      <CompleteOnExplicitConstructorCall:primary().super(1, 2)>;\n" +
+		"      primary().super(1, 2, <CompleteOnName:>, i);\n" +
 		"    }\n" +
 		"  }\n" +
 		"  static Bar x;\n" +
@@ -98,7 +98,7 @@
 		// completeBehind:
 		"this(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnExplicitConstructorCall:primary().this(1, 2)>;",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  public class InnerBar {\n" +
@@ -107,7 +107,7 @@
 		"  }\n" +
 		"  public class SubInnerBar extends InnerBar {\n" +
 		"    SubInnerBar(Bar x) {\n" +
-		"      <CompleteOnExplicitConstructorCall:primary().this(1, 2)>;\n" +
+		"      primary().this(1, 2, <CompleteOnName:>, i);\n" +
 		"    }\n" +
 		"  }\n" +
 		"  static Bar x;\n" +
@@ -140,11 +140,11 @@
 		// completeBehind:
 		"super(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnExplicitConstructorCall:super(1, 2)>;",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  Bar() {\n" +
-		"    <CompleteOnExplicitConstructorCall:super(1, 2)>;\n" +
+		"    super(1, 2, <CompleteOnName:>, i);\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
@@ -171,11 +171,11 @@
 		// completeBehind:
 		"this(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnExplicitConstructorCall:this(1, 2)>;",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  Bar() {\n" +
-		"    <CompleteOnExplicitConstructorCall:this(1, 2)>;\n" +
+		"    this(1, 2, <CompleteOnName:>, i);\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/MethodInvocationCompletionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/MethodInvocationCompletionTest.java
index 38dca1c..3f061ee 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/MethodInvocationCompletionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/MethodInvocationCompletionTest.java
@@ -395,19 +395,19 @@
 		// completeBehind:
 		"fred(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnMessageSend:this.fred(1, 2)>",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  Bar() {\n" +
 		"  }\n" +
 		"  void foo() {\n" +
-		"    <CompleteOnMessageSend:this.fred(1, 2)>;\n" +
+		"    this.fred(1, 2, <CompleteOnName:>, i);\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
 		"",
 		// expectedReplacedSource:
-		"fred(1, 2,",
+		"",
 		// test name
 		"<completion just before last parameter>"
 	);
@@ -555,19 +555,19 @@
 		// completeBehind:
 		"fred(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnMessageSend:bar().fred(1, 2)>",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class X {\n" +
 		"  X() {\n" +
 		"  }\n" +
 		"  void foo() {\n" +
-		"    <CompleteOnMessageSend:bar().fred(1, 2)>;\n" +
+		"    label1: bar().fred(1, 2, <CompleteOnName:>, o);\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
 		"",
 		// expectedReplacedSource:
-		"fred(1, 2,",
+		"",
 		// expectedLabels:
 		new String[] {"label1"},
 		// test name
@@ -588,19 +588,19 @@
 		// completeBehind:
 		"fred(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnMessageSend:fred(1, 2)>",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  Bar() {\n" +
 		"  }\n" +
 		"  void foo() {\n" +
-		"    <CompleteOnMessageSend:fred(1, 2)>;\n" +
+		"    label1: fred(1, 2, <CompleteOnName:>, o);\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
 		"",
 		// expectedReplacedSource:
-		"fred(1, 2,",
+		"",
 		// expectedLabels:
 		new String[] {"label1"},
 		// test name
@@ -813,20 +813,20 @@
 		// completeBehind:
 		"x.fred(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnMessageSend:x.fred(1, 2)>",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  Bar() {\n" +
 		"  }\n" +
 		"  void foo() {\n" +
 		"    X x;\n" +
-		"    <CompleteOnMessageSend:x.fred(1, 2)>;\n" +
+		"    x.fred(1, 2, <CompleteOnName:>, o);\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
 		"",
 		// expectedReplacedSource:
-		"fred(1, 2,",
+		"",
 		// test name
 		"<completion with name receiver and 2 arguments>"
 	);
@@ -846,20 +846,20 @@
 		// completeBehind:
 		"x.fred(1, 2,",
 		// expectedCompletionNodeToString:
-		"<CompleteOnMessageSend:y.x.fred(1, 2)>",
+		"<CompleteOnName:>",
 		// expectedUnitDisplayString:
 		"class Bar {\n" +
 		"  Bar() {\n" +
 		"  }\n" +
 		"  void foo() {\n" +
 		"    X x;\n" +
-		"    <CompleteOnMessageSend:y.x.fred(1, 2)>;\n" +
+		"    y.x.fred(1, 2, <CompleteOnName:>, o);\n" +
 		"  }\n" +
 		"}\n",
 		// expectedCompletionIdentifier:
 		"",
 		// expectedReplacedSource:
-		"fred(1, 2,",
+		"",
 		// test name
 		"<completion with qualified name receiver>"
 	);
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 205b936..3de6495 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
@@ -1393,6 +1393,7 @@
 	    expectedProblemAttributes.put("EnhancedSwitchMissingDefault", new ProblemAttributes(true));
 	    expectedProblemAttributes.put("DuplicateTotalPattern", new ProblemAttributes(true));
 	    expectedProblemAttributes.put("UnexpectedTypeinSwitchPattern", new ProblemAttributes(true));
+	    expectedProblemAttributes.put("ClassExtendFinalRecord", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
 
 	    StringBuffer failures = new StringBuffer();
 		StringBuffer correctResult = new StringBuffer(70000);
@@ -3091,6 +3092,7 @@
 	    expectedProblemAttributes.put("EnhancedSwitchMissingDefault", SKIP);
 	    expectedProblemAttributes.put("DuplicateTotalPattern", SKIP);
 	    expectedProblemAttributes.put("UnexpectedTypeinSwitchPattern", SKIP);
+	    expectedProblemAttributes.put("ClassExtendFinalRecord", SKIP);
 
 	    Map constantNamesIndex = new HashMap();
 		Field[] fields = JavaCore.class.getFields();
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java
index bf28ff2..0a85da8 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2020 GK Software AG, and others.
+ * Copyright (c) 2013, 2021 GK Software SE, and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10325,4 +10325,40 @@
 				"}\n"
 			});
 	}
+	public void testBug576516() {
+		if (this.complianceLevel < ClassFileConstants.JDK11) return; // uses 'var'
+		runConformTest(
+			new String[] {
+				"lib/Base.java",
+				"package lib;\n" +
+				"public class Base {}\n",
+
+				"lib/ClassA.java",
+				"package lib;\n" +
+				"import lib.Holder.Tagging;\n" +
+				"public class ClassA extends Base implements Tagging { }\n",
+
+				"lib/ClassB.java",
+				"package lib;\n" +
+				"import lib.Holder.Tagging;\n" +
+				"public class ClassB extends Base implements Tagging { }\n",
+
+				"lib/Holder.java",
+				"package lib;\n" +
+				"public class Holder  {\n" +
+				"    interface Tagging { }\n" +
+				"}",
+
+				"Test.java",
+				"import java.util.stream.Stream;\n" +
+				"import lib.ClassA;\n" +
+				"import lib.ClassB;\n" +
+				"public class Test {\n" +
+				"\n" +
+				"    public static void main(String[] args) {\n" +
+				"        var builders = Stream.of(new ClassA(), new ClassB());\n" +
+				"    }\n" +
+				"}\n"
+			});
+	}
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
index bffc9f2..c7cf6b1 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
@@ -1046,7 +1046,10 @@
 					"       try {\n" +
 					"		    X x = (X & I & J) o;\n" +
 					"       } catch (ClassCastException e) {\n" +
-					"           System.out.println(e.getMessage());\n" +
+					// Make assertion more robust by producing predictable output for Java 11+:
+					//   - Omit stack trace
+					//   - Cut off class loader name (e.g. 'java.net.URLClassLoader @f3f9f4b') for easier matching
+					"           System.out.println(e.getMessage().replaceFirst(\"(unnamed module of loader).*\", \"$1\"));\n" +
 					"       }\n" +
 					"	}\n" +
 					"}\n",
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java
index 3cf5ff8..c0697c8 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java
@@ -14474,7 +14474,10 @@
 			"		try {\n" +
 			"			OtherResult result = demo.test(new OtherResult());\n" +
 			"		} catch (ClassCastException e) {\n" +
-			"			System.out.println(e.getMessage());\n" + // omit the stack trace for test robustness
+			// Make assertion more robust by producing predictable output for Java 11+:
+			//   - Omit stack trace
+			//   - Cut off class loader name (e.g. 'java.net.URLClassLoader @f3f9f4b') for easier matching
+			"			System.out.println(e.getMessage().replaceFirst(\"(unnamed module of loader).*\", \"$1\"));\n" +
 			"		}\n" +
 			"	}\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 ec57d5f..902493a 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
@@ -9099,5 +9099,24 @@
 			},
 		"0");
 }
+public void testBug576519_001() {
+	this.runNegativeTest(
+		new String[] {
+			"X.java",
+			"class X extends Point{\n"+
+			"  public X(int x, int y){\n"+
+			"     \n" +
+			"  }\n"+
+			"}\n"+
+			"record Point(int x, int y){\n"+
+		"}",
+		},
+		"----------\n" +
+		"1. ERROR in X.java (at line 1)\n" +
+		"	class X extends Point{\n" +
+		"	                ^^^^^\n" +
+		"The record Point cannot be the superclass of X; a record is final and cannot be extended\n" +
+		"----------\n");
+}
 
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java
index 66d81ff..d35f62f 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java
@@ -2230,17 +2230,19 @@
 			new String[] {
 				"X.java",
 				"public class X {\n"+
-				" private static void foo(Object o) {\n"+
+				" public static void foo(Object o) {\n"+
 				"   switch (o) {\n"+
 				"     case null, Integer i  -> System.out.println(0);\n"+
 				"     default -> System.out.println(o);\n"+
 				"   }\n"+
+				" }\n"+
+				" public static void bar(Object o) {\n"+
 				"   Zork();\n"+
 				" }\n"+
 				"}",
 			},
 			"----------\n" +
-			"1. ERROR in X.java (at line 7)\n" +
+			"1. ERROR in X.java (at line 9)\n" +
 			"	Zork();\n" +
 			"	^^^^\n" +
 			"The method Zork() is undefined for the type X\n" +
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TextBlockTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TextBlockTest.java
index efb0f7f..0c60d32 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TextBlockTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TextBlockTest.java
@@ -1550,4 +1550,36 @@
 				getCompilerOptions(),
 				new String[] {"--enable-preview"});
 	}
+	public void testBug575953() {
+		runConformTest(
+				new String[] {
+						"X.java",
+						"public class X {\n" +
+						"    public static void main(String[] args)  {\n" +
+						"    	String TEXT_BLOCK = \"\"\"\n" +
+						"           public class A {\n" +
+						"               public void foo() {\\s\n" +
+						"                   String k = \\\"\"\"\n" +
+						"                       abcdefg\n" +
+						"                       \\\"\"\"\n" +
+						"                   System.out.pri\\\n" +
+						"           ntln(\"abc\");\\s\n" +
+						"               }\n" +
+						"           }\\\n" +
+						"           \"\"\";\n" +
+						"        System.out.println(TEXT_BLOCK);\n" +
+						"    }\n" +
+						"}\n"
+				},
+				"public class A {\n" +
+				"    public void foo() { \n" +
+				"        String k = \"\"\"\n" +
+				"            abcdefg\n" +
+				"            \"\"\"\n" +
+				"        System.out.println(\"abc\"); \n" +
+				"    }\n" +
+				"}",
+				getCompilerOptions(),
+				new String[] {"--enable-preview"});
+	}
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/TestVerifier.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/TestVerifier.java
index 1d7d3eb..18ce0d9 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/TestVerifier.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/TestVerifier.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,8 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Alexander Kriegisch - bug 286316: Set classpath for forked test JVM via
+ *       DataOutputStream instead of JVM parameter; improve file deletion logic
  *******************************************************************************/
 package org.eclipse.jdt.core.tests.util;
 
@@ -17,6 +19,10 @@
 import org.eclipse.jdt.core.tests.runtime.*;
 import java.io.*;
 import java.net.*;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
 /**
  * Verifies that the .class files resulting from a compilation can be loaded
  * in a VM and that they can be run.
@@ -128,222 +134,95 @@
 public String getExecutionError(){
 	return this.errorBuffer.toString();
 }
+
 /**
- * Returns the code of the VerifyTests class.
- *
+ * Default value for {@link VerifyTests} source code, copied and regularly refreshed from original source code
+ * <p>
  * IMPORTANT NOTE: DO NOTE EDIT BUT GENERATE INSTEAD (see below)
- *
- * To generate:
- * - export VerifyTests.java to d:/temp
- * - inspect org.eclipse.jdt.core.tests.util.Util.fileContentToDisplayString("d:/temp/VerifyTests.java", 2, true)
+ * <p>
+ * To generate:<ul>
+ *   <li>export VerifyTests.java to d:/temp</li>
+ *   <li>inspect org.eclipse.jdt.core.tests.util.Util.fileContentToDisplayString("d:/temp/VerifyTests.java", 2, true)</li>
+ * </ul><p>
  */
-private String getVerifyTestsCode() {
-	return
+static final String VERIFY_TEST_CODE_DEFAULT;
+
+static {
+	// Use static initialiser block instead of direct field initialisation, because it permits for code folding in IDEs,
+	// i.e. this huge string can easily be folded away, which minimises scrolling.
+	VERIFY_TEST_CODE_DEFAULT =
 		"/*******************************************************************************\n" +
-		" * Copyright (c) 2000, 2017 IBM Corporation and others.\n" +
-		" * All rights reserved. This program and the accompanying materials\n" +
-		" * are made available under the terms of the Eclipse Public License v1.0\n" +
+		" * Copyright (c) 2000, 2021 IBM Corporation and others.\n" +
+		" *\n" +
+		" * This program and the accompanying materials\n" +
+		" * are made available under the terms of the Eclipse Public License 2.0\n" +
 		" * which accompanies this distribution, and is available at\n" +
-		" * http://www.eclipse.org/legal/epl-v10.html\n" +
+		" * https://www.eclipse.org/legal/epl-2.0/\n" +
+		" *\n" +
+		" * SPDX-License-Identifier: EPL-2.0\n" +
 		" *\n" +
 		" * Contributors:\n" +
 		" *     IBM Corporation - initial API and implementation\n" +
+		" *     Alexander Kriegisch - bug 286316: Get classpath via DataInputStream and\n" +
+		" *         use it in an isolated URLClassLoader, enabling formerly locked\n" +
+		" *         classpath JARs to be closed on Windows\n" +
 		" *******************************************************************************/\n" +
 		"package org.eclipse.jdt.core.tests.util;\n" +
 		"\n" +
 		"import java.io.DataInputStream;\n" +
 		"import java.io.DataOutputStream;\n" +
 		"import java.io.File;\n" +
-		"import java.io.FileInputStream;\n" +
-		"import java.io.FileNotFoundException;\n" +
 		"import java.io.IOException;\n" +
-		"import java.io.InputStream;\n" +
 		"import java.lang.reflect.InvocationTargetException;\n" +
 		"import java.lang.reflect.Method;\n" +
+		"import java.net.MalformedURLException;\n" +
 		"import java.net.Socket;\n" +
-		"import java.util.StringTokenizer;\n" +
+		"import java.net.URL;\n" +
+		"import java.net.URLClassLoader;\n" +
 		"\n" +
-		"/******************************************************\n" +
-		" *\n" +
-		" * IMPORTANT NOTE: If modifying this class, copy the source to TestVerifier#getVerifyTestsCode()\n" +
-		" * (see this method for details)\n" +
-		" *\n" +
-		" ******************************************************/\n" +
-		"\n" +
+		"/**\n" +
+		" * <b>IMPORTANT NOTE:</b> When modifying this class, please copy the source into the static initialiser block for field\n" +
+		" * {@link TestVerifier#VERIFY_TEST_CODE_DEFAULT}. See also {@link TestVerifier#READ_VERIFY_TEST_FROM_FILE}, if you want\n" +
+		" * to dynamically load the source code directly from this file when running tests, which is a convenient way to test if\n" +
+		" * changes in this class work as expected, without the need to update the hard-coded default value every single time\n" +
+		" * during an ongoing refactoring.\n" +
+		" * <p>\n" +
+		" * In order to make the copying job easier, keep this class compatible with Java 5 language level. You may however use\n" +
+		" * things like {@code @Override} for interfaces, {@code assert} (if in a single line), {@code @SuppressWarnings},\n" +
+		" * because {@link TestVerifier#getVerifyTestsCode()} can filter them out dynamically. You should however avoid things\n" +
+		" * like diamonds, multi-catch, catch-with-resources and more recent Java features.\n" +
+		" */\n" +
+		"@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n" +
 		"public class VerifyTests {\n" +
 		"	int portNumber;\n" +
 		"	Socket socket;\n" +
 		"\n" +
-		"/**\n" +
-		" * NOTE: Code copied from junit.util.TestCaseClassLoader.\n" +
-		" *\n" +
-		" * A custom class loader which enables the reloading\n" +
-		" * of classes for each test run. The class loader\n" +
-		" * can be configured with a list of package paths that\n" +
-		" * should be excluded from loading. The loading\n" +
-		" * of these packages is delegated to the system class\n" +
-		" * loader. They will be shared across test runs.\n" +
-		" * <p>\n" +
-		" * The list of excluded package paths is specified in\n" +
-		" * a properties file \"excluded.properties\" that is located in\n" +
-		" * the same place as the TestCaseClassLoader class.\n" +
-		" * <p>\n" +
-		" * <b>Known limitation:</b> the VerifyClassLoader cannot load classes\n" +
-		" * from jar files.\n" +
-		" */\n" +
-		"\n" +
-		"\n" +
-		"public class VerifyClassLoader extends ClassLoader {\n" +
-		"	/** scanned class path */\n" +
-		"	private String[] pathItems;\n" +
-		"\n" +
-		"	/** excluded paths */\n" +
-		"	private String[] excluded= {};\n" +
-		"\n" +
-		"	/**\n" +
-		"	 * Constructs a VerifyClassLoader. It scans the class path\n" +
-		"	 * and the excluded package paths\n" +
-		"	 */\n" +
-		"	public VerifyClassLoader() {\n" +
-		"		super();\n" +
-		"		String classPath= System.getProperty(\"java.class.path\");\n" +
-		"		String separator= System.getProperty(\"path.separator\");\n" +
-		"\n" +
-		"		// first pass: count elements\n" +
-		"		StringTokenizer st= new StringTokenizer(classPath, separator);\n" +
-		"		int i= 0;\n" +
-		"		while (st.hasMoreTokens()) {\n" +
-		"			st.nextToken();\n" +
-		"			i++;\n" +
-		"		}\n" +
-		"		// second pass: split\n" +
-		"		this.pathItems= new String[i];\n" +
-		"		st= new StringTokenizer(classPath, separator);\n" +
-		"		i= 0;\n" +
-		"		while (st.hasMoreTokens()) {\n" +
-		"			this.pathItems[i++]= st.nextToken();\n" +
-		"		}\n" +
-		"\n" +
+		"private static URL[] classPathToURLs(String[] classPath) throws MalformedURLException {\n" +
+		"	URL[] urls = new URL[classPath.length];\n" +
+		"	for (int i = 0; i < classPath.length; i++) {\n" +
+		"		urls[i] = new File(classPath[i]).toURI().toURL();\n" +
 		"	}\n" +
-		"	public java.net.URL getResource(String name) {\n" +
-		"		return ClassLoader.getSystemResource(name);\n" +
-		"	}\n" +
-		"	public InputStream getResourceAsStream(String name) {\n" +
-		"		return ClassLoader.getSystemResourceAsStream(name);\n" +
-		"	}\n" +
-		"	protected boolean isExcluded(String name) {\n" +
-		"		// exclude the \"java\" packages.\n" +
-		"		// They always need to be excluded so that they are loaded by the system class loader\n" +
-		"		if (name.startsWith(\"java\") || name.startsWith(\"[Ljava\"))\n" +
-		"			return true;\n" +
-		"\n" +
-//{ObjectTeams: don't process core OT-classes either (unpacked .class not available on classpath):
-		"		if (name.startsWith(\"org.objectteams\"))\n" +
-		"			return true;\n" +
-		"			\n" +
-		"		if (name.startsWith(\"org.eclipse.objectteams.otre\"))\n" +
-		"			return true;\n" +
-		"			\n" +
-		"		if (name.startsWith(\"org.apache.bcel\"))\n" +
-		"			return true;\n" +
-		"			\n" +
-		"		if (name.startsWith(\"sun\"))\n" + // have a test that needs sun/reflect/SerializationConstructorAccessorImpl
-		"			return true;\n" +
-		"			\n" +
-// SH}
-		"		// exclude the user defined package paths\n" +
-		"		for (int i= 0; i < this.excluded.length; i++) {\n" +
-		"			if (name.startsWith(this.excluded[i])) {\n" +
-		"				return true;\n" +
-		"			}\n" +
-		"		}\n" +
-		"		return false;\n" +
-		"	}\n" +
-		"	public synchronized Class loadClass(String name, boolean resolve)\n" +
-		"		throws ClassNotFoundException {\n" +
-		"\n" +
-		"		Class c= findLoadedClass(name);\n" +
-		"		if (c != null)\n" +
-		"			return c;\n" +
-		"		//\n" +
-		"		// Delegate the loading of excluded classes to the\n" +
-		"		// standard class loader.\n" +
-		"		//\n" +
-		"		if (isExcluded(name)) {\n" +
-		"			try {\n" +
-		"				c= findSystemClass(name);\n" +
-		"				return c;\n" +
-		"			} catch (ClassNotFoundException e) {\n" +
-		"				// keep searching\n" +
-		"			}\n" +
-		"		}\n" +
-		"		File file= locate(name);\n" +
-		"		if (file == null)\n" +
-		"			throw new ClassNotFoundException();\n" +
-		"		byte data[]= loadClassData(file);\n" +
-		"		c= defineClass(name, data, 0, data.length);\n" +
-		"		if (resolve)\n" +
-		"			resolveClass(c);\n" +
-		"		return c;\n" +
-		"	}\n" +
-		"	private byte[] loadClassData(File f) throws ClassNotFoundException {\n" +
-		"		FileInputStream stream = null;\n" +
-		"		try {\n" +
-		"			//System.out.println(\"loading: \"+f.getPath());\n" +
-		"			stream = new FileInputStream(f);\n" +
-		"\n" +
-		"			try {\n" +
-		"				byte[] b= new byte[stream.available()];\n" +
-		"				stream.read(b);\n" +
-		"				return b;\n" +
-		"			}\n" +
-		"			catch (IOException e) {\n" +
-		"				throw new ClassNotFoundException();\n" +
-		"			}\n" +
-		"		}\n" +
-		"		catch (FileNotFoundException e) {\n" +
-		"			throw new ClassNotFoundException();\n" +
-		"		} finally {\n" +
-		"			if (stream != null) {\n" +
-		"				try {\n" +
-		"					stream.close();\n" +
-		"				} catch (IOException e) {\n" +
-		"					/* ignore */\n" +
-		"				}\n" +
-		"			}\n" +
-		"		}\n" +
-		"	}\n" +
-		"	/**\n" +
-		"	 * Locate the given file.\n" +
-		"	 * @return Returns null if file couldn't be found.\n" +
-		"	 */\n" +
-		"	private File locate(String fileName) {\n" +
-		"		if (fileName != null) {\n" +
-		"			fileName= fileName.replace('.', '/')+\".class\";\n" +
-		"			File path= null;\n" +
-		"			for (int i= 0; i < this.pathItems.length; i++) {\n" +
-		"				path= new File(this.pathItems[i], fileName);\n" +
-		"				if (path.exists())\n" +
-		"					return path;\n" +
-		"			}\n" +
-		"		}\n" +
-		"		return null;\n" +
-		"	}\n" +
+		"	return urls;\n" +
 		"}\n" +
 		"\n" +
-		"public void loadAndRun(String className) throws Throwable {\n" +
-		"	//System.out.println(\"Loading \" + className + \"...\");\n" +
-		"	Class testClass = new VerifyClassLoader().loadClass(className);\n" +
-		"	//System.out.println(\"Loaded \" + className);\n" +
+		"public void loadAndRun(String className, String[] classPath) throws Throwable {\n" +
+		"	URLClassLoader urlClassLoader = new URLClassLoader(classPathToURLs(classPath));\n" +
 		"	try {\n" +
-		"		Method main = testClass.getMethod(\"main\", new Class[] {String[].class});\n" +
-		"		//System.out.println(\"Running \" + className);\n" +
-		"		main.invoke(null, new Object[] {new String[] {}});\n" +
-		"		//System.out.println(\"Finished running \" + className);\n" +
-		"	} catch (NoSuchMethodException e) {\n" +
-		"		return;\n" +
-		"	} catch (InvocationTargetException e) {\n" +
-		"		throw e.getTargetException();\n" +
+		"		//System.out.println(\"Loading \" + className + \"...\");\n" +
+		"		Class testClass = urlClassLoader.loadClass(className);\n" +
+		"		//System.out.println(\"Loaded \" + className);\n" +
+		"		try {\n" +
+		"			Method main = testClass.getMethod(\"main\", new Class[] {String[].class});\n" +
+		"			//System.out.println(\"Running \" + className);\n" +
+		"			main.invoke(null, new Object[] {new String[] {}});\n" +
+		"			//System.out.println(\"Finished running \" + className);\n" +
+		"		} catch (NoSuchMethodException e) {\n" +
+		"			return;\n" +
+		"		} catch (InvocationTargetException e) {\n" +
+		"			throw e.getTargetException();\n" +
+		"		}\n" +
+		"	} finally {\n" +
+		"		urlClassLoader.close();\n" +
 		"	}\n" +
 		"}\n" +
 		"public static void main(String[] args) throws IOException {\n" +
@@ -359,35 +238,147 @@
 		"	final DataOutputStream out = new DataOutputStream(this.socket.getOutputStream());\n" +
 		"	while (true) {\n" +
 		"		final String className = in.readUTF();\n" +
+		"		final int length = in.readInt();\n" +
+		"		final String[] classPath = new String[length];\n" +
+		"		for (int i = 0; i < length; i++) {\n" +
+		"			classPath[i] = in.readUTF();\n" +
+		"		}\n" +
 		"		Thread thread = new Thread() {\n" +
+		"			@Override\n" +
 		"			public void run() {\n" +
 		"				try {\n" +
-		"					loadAndRun(className);\n" +
+		"					loadAndRun(className, classPath);\n" +
 		"					out.writeBoolean(true);\n" +
-		"					System.err.println(VerifyTests.class.getName());\n" +
 		"					System.out.println(VerifyTests.class.getName());\n" +
+		"					System.err.println(VerifyTests.class.getName());\n" +
 		"				} catch (Throwable e) {\n" +
 		"					e.printStackTrace();\n" +
 		"					try {\n" +
-		"						System.err.println(VerifyTests.class.getName());\n" +
-		"						System.out.println(VerifyTests.class.getName());\n" +
 		"						out.writeBoolean(false);\n" +
+		"						System.out.println(VerifyTests.class.getName());\n" +
+		"						System.err.println(VerifyTests.class.getName());\n" +
 		"					} catch (IOException e1) {\n" +
 		"						e1.printStackTrace();\n" +
 		"					}\n" +
 		"				}\n" +
+		"				// Flush all streams, in case the test executor VM is shut down before\n" +
+		"				// the controlling VM receives the responses it depends on\n" +
 		"				try {\n" +
 		"					out.flush();\n" +
 		"				} catch (IOException e) {\n" +
 		"					e.printStackTrace();\n" +
 		"				}\n" +
+		"				System.out.flush();\n" +
+		"				System.err.flush();\n" +
 		"			}\n" +
 		"		};\n" +
 		"		thread.start();\n" +
 		"	}\n" +
 		"}\n" +
-		"}";
+		"}\n";
 }
+
+/**
+ * Activate, if you want to read the {@link VerifyTests} source code directly from the file system in
+ * {@link #getVerifyTestsCode()}, e.g. during development while refactoring the source code.
+ */
+public static boolean READ_VERIFY_TEST_FROM_FILE = false;
+/**
+ * Adjust, if in {@link #READ_VERIFY_TEST_FROM_FILE} mode method {@link #getVerifyTestsCode()} cannot find
+ * the source file based on the current directory. In that case, set the correct JDT Core project base
+ * directory as PROJECT_BASE_DIR environment variable, so that the 'org.eclipse.jdt.core.tests.compiler/src'
+ * sub-directory can be found from there.
+ */
+public static String PROJECT_BASE_DIR = System.getenv("PROJECT_BASE_DIR");
+
+// Cached value for VerifyTests.java source code, read only once, either directly from the source code directory or
+// from VERIFY_TEST_CODE_DEFAULT
+private static String verifyTestCode;
+
+// Helper object for guarding 'verifyTestCode' with 'synchronized (verifyTestCodeLock)', in case tests are to be run in
+// parallel
+private static final Object verifyTestCodeLock = new Object();
+
+/**
+ * Returns {@link VerifyTests} source code, to be used as a boot-strapping class in forked test JVMs
+ * <p>
+ * Optionally, you can use {@link #READ_VERIFY_TEST_FROM_FILE} in order to read the source code from the project's
+ * source directory. If it is not found automatically, you may also adjust {@link #PROJECT_BASE_DIR}. Both values are
+ * public and writable during runtime.
+ * <p>
+ * <b>Caveat:</b> The return value is only lazily initialised once, then cached. If you change
+ * {@link #READ_VERIFY_TEST_FROM_FILE} after calling this method for the first time, the return value will not change
+ * anymore.
+ *
+ * @return {@link VerifyTests} source code, filtered by {@link #filterSourceCode(Stream)}
+ */
+String getVerifyTestsCode() {
+	synchronized (verifyTestCodeLock) {
+		if (verifyTestCode == null) {
+			if (READ_VERIFY_TEST_FROM_FILE) {
+				String sourceFile = "src/org/eclipse/jdt/core/tests/util/VerifyTests.java";
+				if (!new File(sourceFile).exists()) {
+					sourceFile = PROJECT_BASE_DIR + "/org.eclipse.jdt.core.tests.compiler/" + sourceFile;
+				}
+				try (BufferedReader reader = new BufferedReader(new FileReader(sourceFile))) {
+					verifyTestCode = filterSourceCode(reader.lines());
+				}
+				catch (IOException e) {
+					System.out.println("WARNING: Cannot read & filter VerifyTests source code from file, using default value");
+					System.out.println("	- exception: " + e);
+				}
+			}
+		}
+		if (verifyTestCode == null) {
+			try (BufferedReader reader = new BufferedReader(new StringReader(VERIFY_TEST_CODE_DEFAULT))) {
+				verifyTestCode = filterSourceCode(reader.lines());
+			}
+			catch (IOException e) {
+				System.out.println("WARNING: Cannot filter VerifyTests source code default value, using unfiltered value");
+				System.out.println("	- exception: " + e);
+				verifyTestCode = VERIFY_TEST_CODE_DEFAULT;
+			}
+		}
+		return verifyTestCode;
+	}
+}
+
+/**
+ * Filter some elements incompatible with Java source level 1.5 from source code
+ * <p>
+ * This method cannot convert things like catch-with-resources or other language elements back to Java 1.5, you have to
+ * take care of keeping the source code backward compatible by yourself. But a few things you can still use in the
+ * source code, such as {@code @SuppressWarnings}, {@code @Override} in interfaces or single-line {@code assert}.
+ *
+ * @param sourceCodeLines stream of source code lines
+ * @return filtered source code file as a string
+ */
+private String filterSourceCode(Stream<String> sourceCodeLines) {
+	return sourceCodeLines
+		.filter(s -> !(s.contains("@SuppressWarnings") || s.contains("@Override") || s.contains("assert ")))
+		.collect(Collectors.joining("\n"));
+}
+
+/**
+ * Remove non-essential parts of the test JVM classpath
+ * <p>
+ * The classpath for the forked test JVM should only contain JDK paths and the 'verifier' subdirectory where the
+ * {@link VerifyTests} class boot-strapping the test resides, because those need to be present during JVM start-up.
+ * Other parts of the classpath are stripped off, because they are to be communicated to the forked JVM via direct
+ * socket communication.
+ *
+ * @param classPath full classpath
+ * @return minimal classpath necessary for forked test JVM boot-strapping
+ */
+private String[] getMinimalClassPath(String[] classPath) {
+return Arrays.stream(classPath)
+	.filter(s -> {
+		String path = s.replace('\\', '/');
+		return !path.contains("/comptest/") || path.endsWith("/verifier");
+	})
+	.toArray(String[]::new);
+}
+
 private void launchAndRun(String className, String[] classpaths, String[] programArguments, String[] vmArguments) {
 	// we won't reuse the vm, shut the existing one if running
 	if (this.vm != null) {
@@ -492,7 +483,7 @@
 	String verifierDir = Util.getOutputDirectory() + File.separator + "verifier";
 	compileVerifyTests(verifierDir);
 	cp[length] = verifierDir;
-	launcher.setClassPath(cp);
+	launcher.setClassPath(getMinimalClassPath(cp));
 	launcher.setVMPath(Util.getJREDirectory());
 	if (vmArguments != null) {
 		String[] completeVmArguments = new String[vmArguments.length + 1];
@@ -582,11 +573,17 @@
  * Loads and runs the given class.
  * Return whether no exception was thrown while running the class.
  */
-private boolean loadAndRun(String className) {
+private boolean loadAndRun(String className, String[] classPath) {
 	if (this.socket != null) {
 		try {
 			DataOutputStream out = new DataOutputStream(this.socket.getOutputStream());
 			out.writeUTF(className);
+				if (classPath == null)
+					classPath = new String[0];
+				out.writeInt(classPath.length);
+				for (String classpath : classPath) {
+					out.writeUTF(classpath);
+				}
 			DataInputStream in = new DataInputStream(this.socket.getInputStream());
 			try {
 				boolean result = in.readBoolean();
@@ -649,7 +646,7 @@
 	this.errorBuffer = new StringBuffer();
 	if (this.reuseVM && programArguments == null) {
 		launchVerifyTestsIfNeeded(classpaths, vmArguments);
-		loadAndRun(className);
+		loadAndRun(className, classpaths);
 	} else {
 		launchAndRun(className, classpaths, programArguments, vmArguments);
 	}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/VerifyTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/VerifyTests.java
index 67a3d17..6ec6faa 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/VerifyTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/VerifyTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,192 +10,66 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Alexander Kriegisch - bug 286316: Get classpath via DataInputStream and
+ *         use it in an isolated URLClassLoader, enabling formerly locked
+ *         classpath JARs to be closed on Windows
  *******************************************************************************/
 package org.eclipse.jdt.core.tests.util;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.net.MalformedURLException;
 import java.net.Socket;
-import java.util.StringTokenizer;
+import java.net.URL;
+import java.net.URLClassLoader;
 
-/******************************************************
- *
- * IMPORTANT NOTE: If modifying this class, copy the source to TestVerifier#getVerifyTestsCode()
- * (see this method for details)
- *
- ******************************************************/
-
+/**
+ * <b>IMPORTANT NOTE:</b> When modifying this class, please copy the source into the static initialiser block for field
+ * {@link TestVerifier#VERIFY_TEST_CODE_DEFAULT}. See also {@link TestVerifier#READ_VERIFY_TEST_FROM_FILE}, if you want
+ * to dynamically load the source code directly from this file when running tests, which is a convenient way to test if
+ * changes in this class work as expected, without the need to update the hard-coded default value every single time
+ * during an ongoing refactoring.
+ * <p>
+ * In order to make the copying job easier, keep this class compatible with Java 5 language level. You may however use
+ * things like {@code @Override} for interfaces, {@code assert} (if in a single line), {@code @SuppressWarnings},
+ * because {@link TestVerifier#getVerifyTestsCode()} can filter them out dynamically. You should however avoid things
+ * like diamonds, multi-catch, catch-with-resources and more recent Java features.
+ */
 @SuppressWarnings({ "unchecked", "rawtypes" })
 public class VerifyTests {
 	int portNumber;
 	Socket socket;
 
-/**
- * NOTE: Code copied from junit.util.TestCaseClassLoader.
- *
- * A custom class loader which enables the reloading
- * of classes for each test run. The class loader
- * can be configured with a list of package paths that
- * should be excluded from loading. The loading
- * of these packages is delegated to the system class
- * loader. They will be shared across test runs.
- * <p>
- * The list of excluded package paths is specified in
- * a properties file "excluded.properties" that is located in
- * the same place as the TestCaseClassLoader class.
- * <p>
- * <b>Known limitation:</b> the VerifyClassLoader cannot load classes
- * from jar files.
- */
-
-
-public class VerifyClassLoader extends ClassLoader {
-	/** scanned class path */
-	private String[] pathItems;
-
-	/** excluded paths */
-	private String[] excluded= {};
-
-	/**
-	 * Constructs a VerifyClassLoader. It scans the class path
-	 * and the excluded package paths
-	 */
-	public VerifyClassLoader() {
-		super();
-		String classPath= System.getProperty("java.class.path");
-		String separator= System.getProperty("path.separator");
-
-		// first pass: count elements
-		StringTokenizer st= new StringTokenizer(classPath, separator);
-		int i= 0;
-		while (st.hasMoreTokens()) {
-			st.nextToken();
-			i++;
-		}
-		// second pass: split
-		this.pathItems= new String[i];
-		st= new StringTokenizer(classPath, separator);
-		i= 0;
-		while (st.hasMoreTokens()) {
-			this.pathItems[i++]= st.nextToken();
-		}
-
+private static URL[] classPathToURLs(String[] classPath) throws MalformedURLException {
+	URL[] urls = new URL[classPath.length];
+	for (int i = 0; i < classPath.length; i++) {
+		urls[i] = new File(classPath[i]).toURI().toURL();
 	}
-	@Override
-	public java.net.URL getResource(String name) {
-		return ClassLoader.getSystemResource(name);
-	}
-	@Override
-	public InputStream getResourceAsStream(String name) {
-		return ClassLoader.getSystemResourceAsStream(name);
-	}
-	protected boolean isExcluded(String name) {
-		// exclude the "java" packages.
-		// They always need to be excluded so that they are loaded by the system class loader
-		if (name.startsWith("java") || name.startsWith("[Ljava"))
-			return true;
-
-		// exclude the user defined package paths
-		for (int i= 0; i < this.excluded.length; i++) {
-			if (name.startsWith(this.excluded[i])) {
-				return true;
-			}
-		}
-		return false;
-	}
-	@Override
-	public synchronized Class loadClass(String name, boolean resolve)
-		throws ClassNotFoundException {
-
-		Class c= findLoadedClass(name);
-		if (c != null)
-			return c;
-		//
-		// Delegate the loading of excluded classes to the
-		// standard class loader.
-		//
-		if (isExcluded(name)) {
-			try {
-				c= findSystemClass(name);
-				return c;
-			} catch (ClassNotFoundException e) {
-				// keep searching
-			}
-		}
-		File file= locate(name);
-		if (file == null)
-			throw new ClassNotFoundException();
-		byte data[]= loadClassData(file);
-		c= defineClass(name, data, 0, data.length);
-		if (resolve)
-			resolveClass(c);
-		return c;
-	}
-	private byte[] loadClassData(File f) throws ClassNotFoundException {
-		FileInputStream stream = null;
-		try {
-			//System.out.println("loading: "+f.getPath());
-			stream = new FileInputStream(f);
-
-			try {
-				byte[] b= new byte[stream.available()];
-				stream.read(b);
-				return b;
-			}
-			catch (IOException e) {
-				throw new ClassNotFoundException();
-			}
-		}
-		catch (FileNotFoundException e) {
-			throw new ClassNotFoundException();
-		} finally {
-			if (stream != null) {
-				try {
-					stream.close();
-				} catch (IOException e) {
-					/* ignore */
-				}
-			}
-		}
-	}
-	/**
-	 * Locate the given file.
-	 * @return Returns null if file couldn't be found.
-	 */
-	private File locate(String fileName) {
-		if (fileName != null) {
-			fileName= fileName.replace('.', '/')+".class";
-			File path= null;
-			for (int i= 0; i < this.pathItems.length; i++) {
-				path= new File(this.pathItems[i], fileName);
-				if (path.exists())
-					return path;
-			}
-		}
-		return null;
-	}
+	return urls;
 }
 
-public void loadAndRun(String className) throws Throwable {
-	//System.out.println("Loading " + className + "...");
-	Class testClass = new VerifyClassLoader().loadClass(className);
-	//System.out.println("Loaded " + className);
+public void loadAndRun(String className, String[] classPath) throws Throwable {
+	URLClassLoader urlClassLoader = new URLClassLoader(classPathToURLs(classPath));
 	try {
-		Method main = testClass.getMethod("main", new Class[] {String[].class});
-		//System.out.println("Running " + className);
-		main.invoke(null, new Object[] {new String[] {}});
-		//System.out.println("Finished running " + className);
-	} catch (NoSuchMethodException e) {
-		return;
-	} catch (InvocationTargetException e) {
-		throw e.getTargetException();
+		//System.out.println("Loading " + className + "...");
+		Class testClass = urlClassLoader.loadClass(className);
+		//System.out.println("Loaded " + className);
+		try {
+			Method main = testClass.getMethod("main", new Class[] {String[].class});
+			//System.out.println("Running " + className);
+			main.invoke(null, new Object[] {new String[] {}});
+			//System.out.println("Finished running " + className);
+		} catch (NoSuchMethodException e) {
+			return;
+		} catch (InvocationTargetException e) {
+			throw e.getTargetException();
+		}
+	} finally {
+		urlClassLoader.close();
 	}
 }
 public static void main(String[] args) throws IOException {
@@ -211,11 +85,16 @@
 	final DataOutputStream out = new DataOutputStream(this.socket.getOutputStream());
 	while (true) {
 		final String className = in.readUTF();
+		final int length = in.readInt();
+		final String[] classPath = new String[length];
+		for (int i = 0; i < length; i++) {
+			classPath[i] = in.readUTF();
+		}
 		Thread thread = new Thread() {
 			@Override
 			public void run() {
 				try {
-					loadAndRun(className);
+					loadAndRun(className, classPath);
 					out.writeBoolean(true);
 					System.out.println(VerifyTests.class.getName());
 					System.err.println(VerifyTests.class.getName());
diff --git a/org.eclipse.jdt.core.tests.model/JCL/jclMin1.8src.zip b/org.eclipse.jdt.core.tests.model/JCL/jclMin1.8src.zip
index 1aa9c7b..8b7670a 100644
--- a/org.eclipse.jdt.core.tests.model/JCL/jclMin1.8src.zip
+++ b/org.eclipse.jdt.core.tests.model/JCL/jclMin1.8src.zip
Binary files differ
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
index 076d523..40b9217 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
@@ -876,6 +876,17 @@
 		);
 	}
 
+	protected void assertResourceOnClasspathEntry(IJavaProject project, IResource resource, String path) {
+		IClasspathEntry cp = project.findContainingClasspathEntry(resource);
+		assertNotNull("IClasspathEntry exists for the resource", cp);
+		assertEquals("In the expected classpath entry", path, cp.getPath().toPortableString());
+	}
+
+	protected void assertResourceNotOnClasspathEntry(IJavaProject project, IResource resource) {
+		IClasspathEntry cp = project.findContainingClasspathEntry(resource);
+		assertNull("IClasspathEntry does not exists for the resource", cp);
+	}
+
 	protected void assertResourceTreeEquals(String message, String expected, Object[] resources) throws CoreException {
 		sortResources(resources);
 		StringBuffer buffer = new StringBuffer();
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java
index 8590fb3..1d51456 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java
@@ -18,6 +18,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
+import org.eclipse.jdt.core.tests.compiler.CharDeduplicationTest;
 import org.eclipse.jdt.core.tests.compiler.map.CharArrayMapperTest;
 import org.eclipse.jdt.core.tests.junit.extension.TestCase;
 
@@ -229,6 +230,8 @@
 		JavaModelManagerTests.class,
 
 		CharArrayMapperTest.class,
+
+		CharDeduplicationTest.class,
 	};
 
 	Class[] deprecatedClasses = getDeprecatedJDOMTestClasses();
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionContextTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionContextTests.java
index 18fda7a..0a9a3e9 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionContextTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionContextTests.java
@@ -2489,8 +2489,8 @@
 		"completion range=["+(tokenStart)+", "+(tokenEnd)+"]\n" +
 		"completion token=\"\"\n" +
 		"completion token kind=TOKEN_KIND_NAME\n" +
-		"expectedTypesSignatures=null\n" +
-		"expectedTypesKeys=null\n"+
+		"expectedTypesSignatures={I}\n" +
+		"expectedTypesKeys={I}\n"+
 		"completion token location=UNKNOWN",
 		result.context);
 }
@@ -2545,8 +2545,8 @@
 		"completion range=["+(tokenStart)+", "+(tokenEnd)+"]\n" +
 		"completion token=\"\"\n" +
 		"completion token kind=TOKEN_KIND_NAME\n" +
-		"expectedTypesSignatures=null\n" +
-		"expectedTypesKeys=null\n"+
+		"expectedTypesSignatures={I}\n" +
+		"expectedTypesKeys={I}\n"+
 		"completion token location=UNKNOWN",
 		result.context);
 }
@@ -2601,8 +2601,8 @@
 		"completion range=["+(tokenStart)+", "+(tokenEnd)+"]\n" +
 		"completion token=\"\"\n" +
 		"completion token kind=TOKEN_KIND_NAME\n" +
-		"expectedTypesSignatures=null\n" +
-		"expectedTypesKeys=null\n"+
+		"expectedTypesSignatures={I}\n" +
+		"expectedTypesKeys={I}\n"+
 		"completion token location=UNKNOWN",
 		result.context);
 }
@@ -2657,8 +2657,8 @@
 		"completion range=["+(tokenStart)+", "+(tokenEnd)+"]\n" +
 		"completion token=\"\"\n" +
 		"completion token kind=TOKEN_KIND_NAME\n" +
-		"expectedTypesSignatures=null\n" +
-		"expectedTypesKeys=null\n"+
+		"expectedTypesSignatures={I}\n" +
+		"expectedTypesKeys={I}\n"+
 		"completion token location=UNKNOWN",
 		result.context);
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java
index cd84004..b0e1172 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java
@@ -13324,6 +13324,7 @@
 	int tEnd = tStart;
 
 	assertResults(
+		"hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, replace["+tStart+", "+tEnd+"], token["+tStart+", "+tEnd+"], 52}\n" +
 		"xBar[METHOD_REF]{CompletionPrefixMethodName3.this.xBar(1,, LCompletionPrefixMethodName3;, (II)I, xBar, (a, b), replace["+rStart1+", "+rEnd1+"], token["+tStart+", "+tEnd+"], "+(R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_EXACT_NAME+ R_NON_RESTRICTED)+"}\n"+
 		"xBar[METHOD_REF]{, LCompletionPrefixMethodName3$classFoo;, (II)I, xBar, (a, b), replace["+rStart2+", "+rEnd2+"], token["+tStart+", "+tEnd+"], "+(R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_EXACT_NAME + R_UNQUALIFIED + R_NON_RESTRICTED)+"}",
 		requestor.getResults());
@@ -19294,9 +19295,7 @@
 			"StaticMembers.StaticClazz[TYPE_REF]{StaticClazz, test, Ltest.StaticMembers$StaticClazz;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}\n" +
 			"class[FIELD_REF]{class, null, Ljava.lang.Class;, class, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}\n" +
 			"staticField[FIELD_REF]{staticField, Ltest.StaticMembers;, I, staticField, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}\n" +
-			"staticMethod[METHOD_REF]{staticMethod(), Ltest.StaticMembers;, ()I, staticMethod, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}\n" +
-			"super[KEYWORD]{super, null, null, super, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}\n" +
-			"this[KEYWORD]{this, null, null, this, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}",
+			"staticMethod[METHOD_REF]{staticMethod(), Ltest.StaticMembers;, ()I, staticMethod, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}",
 			requestor.getResults());
 }
 //https://bugs.eclipse.org/bugs/show_bug.cgi?id=99631
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java
index 90aad65..a3c593d 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java
@@ -26,6 +26,7 @@
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.core.eval.IEvaluationContext;
 import org.eclipse.jdt.internal.codeassist.RelevanceConstants;
 
@@ -242,7 +243,8 @@
 	int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
 	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
 	assertResults(
-			"argument[LOCAL_VARIABLE_REF]{argument, null, Ljava.lang.Object;, argument, null, " + (R_DEFAULT + 21) + "}", // FIXME should be "I" and 22 like test006
+			"argument[LOCAL_VARIABLE_REF]{argument, null, Ljava.lang.Object;, argument, null, 51}\n" // FIXME should be "I" and 22 like test006
+			+ "[LAMBDA_EXPRESSION]{->, LI;, (I)I, foo, (x), 89}",
 			requestor.getResults());
 }
 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=405126, [1.8][code assist] Lambda parameters incorrectly recovered as fields.
@@ -5948,8 +5950,8 @@
 	int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
 	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
 	String result = requestor.getResults();
-	assertResults("getMinimum[METHOD_REF]{, Ljava.util.Calendar;, (I)I, getMinimum, (arg0), 86}",
-			result);
+	assertResults("num[FIELD_REF]{num, LLambdaFreeze2;, I, num, null, 52}\n"
+			+ "getMinimum[METHOD_REF]{, Ljava.util.Calendar;, (I)I, getMinimum, (arg0), 86}", result);
 }
 public void testBug574912_comment6b() throws JavaModelException {
 	this.workingCopies = new ICompilationUnit[1];
@@ -6024,4 +6026,163 @@
 			"completion token location={STATEMENT_START}", // this is required for sysout template proposal
 			requestor.getContext());
 }
+public void testBug575149_expectOverloadedMethodsAndVariablesRankedWithExpectedType() throws JavaModelException {
+	this.workingCopies = new ICompilationUnit[1];
+	this.workingCopies[0] = getWorkingCopy(
+			"Completion/src/Bug443091.java",
+			"import java.util.function.Consumer;\n" +
+			"import java.util.function.Function;\n" +
+			"\n" +
+			"public class Bug443091 {\n" +
+			"	private void foo() {\n" +
+			" 		Consumer<Integer> capture = null;\n" +
+			"		forEach()" +
+			"	}\n" +
+			"	private void forEach(Consumer<Integer> in) {}\n" +
+			"	private void forEach(Function<Integer, String> in) {}\n" +
+			"}\n");
+
+	CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+	requestor.allowAllRequiredProposals();
+	String str = this.workingCopies[0].getSource();
+	String completeBehind = "forEach(";
+	int cursorLocation = str.indexOf(completeBehind) + completeBehind.length();
+	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+	String result = requestor.getResults();
+	assertResults(
+			"capture[LOCAL_VARIABLE_REF]{capture, null, Ljava.util.function.Consumer<Ljava.lang.Integer;>;, capture, null, 52}\n"
+			+ "forEach[METHOD_REF]{, LBug443091;, (Ljava.util.function.Consumer<Ljava.lang.Integer;>;)V, forEach, (in), 56}\n"
+			+ "forEach[METHOD_REF]{, LBug443091;, (Ljava.util.function.Function<Ljava.lang.Integer;Ljava.lang.String;>;)V, forEach, (in), 56}\n"
+			+ "[LAMBDA_EXPRESSION]{->, Ljava.util.function.Function<Ljava.lang.Integer;Ljava.lang.String;>;, (Ljava.lang.Integer;)Ljava.lang.String;, apply, (arg0), 89}\n"
+			+ "[LAMBDA_EXPRESSION]{->, Ljava.util.function.Consumer<Ljava.lang.Integer;>;, (Ljava.lang.Integer;)V, accept, (t), 89}",
+			result);
+	assertTrue("expected type signatures don't match", CharOperation.equals(requestor.getExpectedTypesSignatures(),
+			new char[][] {"Ljava.util.function.Function<Ljava.lang.Integer;Ljava.lang.String;>;".toCharArray(),
+			"Ljava.util.function.Consumer<Ljava.lang.Integer;>;".toCharArray()}, true));
+}
+public void testBug575149_expectRemainingOverloadedMethodsMatchingFilledArguments() throws JavaModelException {
+	this.workingCopies = new ICompilationUnit[1];
+	this.workingCopies[0] = getWorkingCopy(
+			"Completion/src/Bug443091.java",
+			"import java.util.function.Consumer;\n" +
+			"import java.util.function.Function;\n" +
+			"\n" +
+			"public class Bug443091 {\n" +
+			"	private void foo() {\n" +
+			" 		Consumer<Integer> capture = null;\n" +
+			"		forEach(capture, )" +
+			"	}\n" +
+			"	private void forEach(Consumer<Integer> in) {}\n" +
+			"	private void forEach(Consumer<Integer> in, Integer limit) {}\n" +
+			"}\n");
+
+	CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+	requestor.allowAllRequiredProposals();
+	String str = this.workingCopies[0].getSource();
+	String completeBehind = "forEach(capture,";
+	int cursorLocation = str.indexOf(completeBehind) + completeBehind.length();
+	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+	String result = requestor.getResults();
+	assertResults("hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 52}\n"
+			+ "forEach[METHOD_REF]{, LBug443091;, (Ljava.util.function.Consumer<Ljava.lang.Integer;>;)V, forEach, (in), 56}\n"
+			+ "forEach[METHOD_REF]{, LBug443091;, (Ljava.util.function.Consumer<Ljava.lang.Integer;>;Ljava.lang.Integer;)V, forEach, (in, limit), 56}",
+			result);
+	assertTrue("expected type signatures don't match", CharOperation.equals(requestor.getExpectedTypesSignatures(), new char[][] {"Ljava.lang.Integer;".toCharArray()}, true));
+}
+public void testBug575149_expectOverloadsOverEnumLiterals() throws JavaModelException {
+	this.workingCopies = new ICompilationUnit[1];
+	this.workingCopies[0] = getWorkingCopy(
+			"Completion/src/Bug443091.java",
+			"import java.util.function.Consumer;\n" +
+			"import java.util.function.Function;\n" +
+			"\n" +
+			"public class Bug443091 {\n" +
+			"	private void foo() {\n" +
+			" 		Consumer<Integer> capture = null;\n" +
+			"		forEach(capture, )" +
+			"	}\n" +
+			"	private Thread.State defaultState() { return null;} \n" +
+			"	private void forEach(Consumer<Integer> in, Thread.State state) {}\n" +
+			"	private void forEach(Consumer<Integer> in, Thread.State state, Integer limit) {}\n" +
+			"}\n");
+
+	CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+	requestor.allowAllRequiredProposals();
+	String str = this.workingCopies[0].getSource();
+	String completeBehind = "forEach(capture,";
+	int cursorLocation = str.indexOf(completeBehind) + completeBehind.length();
+	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+	String result = requestor.getResults();
+	assertResults("BLOCKED[FIELD_REF]{State.BLOCKED, Ljava.lang.Thread$State;, Ljava.lang.Thread$State;, BLOCKED, null, 49}\n" +
+			"NEW[FIELD_REF]{State.NEW, Ljava.lang.Thread$State;, Ljava.lang.Thread$State;, NEW, null, 49}\n" +
+			"RUNNABLE[FIELD_REF]{State.RUNNABLE, Ljava.lang.Thread$State;, Ljava.lang.Thread$State;, RUNNABLE, null, 49}\n" +
+			"TERMINATED[FIELD_REF]{State.TERMINATED, Ljava.lang.Thread$State;, Ljava.lang.Thread$State;, TERMINATED, null, 49}\n" +
+			"TIMED_WAITING[FIELD_REF]{State.TIMED_WAITING, Ljava.lang.Thread$State;, Ljava.lang.Thread$State;, TIMED_WAITING, null, 49}\n" +
+			"WAITING[FIELD_REF]{State.WAITING, Ljava.lang.Thread$State;, Ljava.lang.Thread$State;, WAITING, null, 49}\n" +
+			"defaultState[METHOD_REF]{defaultState(), LBug443091;, ()Ljava.lang.Thread$State;, defaultState, null, 52}\n" +
+			"forEach[METHOD_REF]{, LBug443091;, (Ljava.util.function.Consumer<Ljava.lang.Integer;>;Ljava.lang.Thread$State;)V, forEach, (in, state), 56}\n" +
+			"forEach[METHOD_REF]{, LBug443091;, (Ljava.util.function.Consumer<Ljava.lang.Integer;>;Ljava.lang.Thread$State;Ljava.lang.Integer;)V, forEach, (in, state, limit), 56}",
+			result);
+	assertTrue("expected type signatures don't match", CharOperation.equals(requestor.getExpectedTypesSignatures(), new char[][] {"Ljava.lang.Thread$State;".toCharArray()}, true));
+}
+public void testBug443091_expectLambdaCompletions_forFunctionalInterfaceArgumentAssignment() throws JavaModelException {
+	this.workingCopies = new ICompilationUnit[1];
+	this.workingCopies[0] = getWorkingCopy(
+			"Completion/src/Bug443091.java",
+			"import java.util.function.Consumer;\n" +
+			"\n" +
+			"public class Bug443091 {\n" +
+			"	private void foo() {\n" +
+			"		forEach(capture)" +
+			"	}\n" +
+			"	private void forEach(Consumer<Integer> in) {}\n" +
+			"}\n");
+
+	CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+	requestor.allowAllRequiredProposals();
+	String str = this.workingCopies[0].getSource();
+	String completeBehind = "forEach(";
+	int cursorLocation = str.indexOf(completeBehind) + completeBehind.length();
+	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+	String result = requestor.getResults();
+	assertResults("forEach[METHOD_REF]{, LBug443091;, (Ljava.util.function.Consumer<Ljava.lang.Integer;>;)V, forEach, (in), 56}\n"
+			+ "[LAMBDA_EXPRESSION]{->, Ljava.util.function.Consumer<Ljava.lang.Integer;>;, (Ljava.lang.Integer;)V, accept, (t), 89}",
+			result);
+}
+public void testBug443091_expectLambdaCompletions_forFunctionalInterfaceVariableAssigments() throws JavaModelException {
+	this.workingCopies = new ICompilationUnit[1];
+	this.workingCopies[0] = getWorkingCopy(
+			"Completion/src/Bug443091.java",
+			"import java.util.function.Consumer;\n" +
+			"\n" +
+			"public class Bug443091 {\n" +
+			"	private void foo() {\n" +
+			" 		Consumer<Integer> in = \n" +
+			"	}\n" +
+			"}\n");
+
+	CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+	requestor.allowAllRequiredProposals();
+	String str = this.workingCopies[0].getSource();
+	String completeBehind = "in =";
+	int cursorLocation = str.indexOf(completeBehind) + completeBehind.length();
+	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+	String result = requestor.getResults();
+	assertResults("finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 47}\n"
+			+ "foo[METHOD_REF]{foo(), LBug443091;, ()V, foo, null, 47}\n"
+			+ "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 47}\n"
+			+ "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 47}\n"
+			+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 47}\n"
+			+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 47}\n"
+			+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 47}\n"
+			+ "Bug443091[TYPE_REF]{Bug443091, , LBug443091;, null, null, 52}\n"
+			+ "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 52}\n"
+			+ "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 52}\n"
+			+ "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, getClass, null, 52}\n"
+			+ "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 52}\n"
+			+ "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 52}\n"
+			+ "Consumer<java.lang.Integer>[TYPE_REF]{Consumer, java.util.function, Ljava.util.function.Consumer<Ljava.lang.Integer;>;, null, null, 82}\n"
+			+ "[LAMBDA_EXPRESSION]{->, Ljava.util.function.Consumer<Ljava.lang.Integer;>;, (Ljava.lang.Integer;)V, accept, (t), 89}",
+			result);
+}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests2.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests2.java
index 9ac305f..ae1ae06 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests2.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests2.java
@@ -6489,4 +6489,110 @@
 		deleteProject("P");
 	}
 }
+public void testBug575562_AccessRestrictionCheck_ENABLED() throws Exception {
+	Hashtable oldOptions = JavaCore.getOptions();
+	try {
+		Hashtable options = new Hashtable(oldOptions);
+		options.put(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, JavaCore.ERROR);
+		options.put(JavaCore.CODEASSIST_FORBIDDEN_REFERENCE_CHECK, JavaCore.ENABLED);
+		options.put(JavaCore.CODEASSIST_DISCOURAGED_REFERENCE_CHECK, JavaCore.DISABLED);
+		JavaCore.setOptions(options);
+		setUpJavaProject("AccessRestrictions", "1.4", false);
+		createJavaProject(
+				"P1",
+				new String[] {"src"},
+				new String[] {"JCL_LIB", "/AccessRestrictions/lib.jar"},
+				new String[][]{{}, {}},
+				new String[][]{{}, {"**/*"}},
+				null/*no project*/,
+				null/*no inclusion pattern*/,
+				null/*no exclusion pattern*/,
+				null/*no exported project*/,
+				"bin",
+				null/*no source outputs*/,
+				null/*no inclusion pattern*/,
+				null/*no exclusion pattern*/,
+				"1.4");
+		this.createFolder("/P1/src/p11");
+		this.createFile(
+				"/P1/src/p11/Y11.java",
+				"package p11;\n"+
+				"public class YY11 {\n"+
+				"  void foo() {\n"+
+				"    X\n"+
+				"  }\n"+
+				"}");
+		waitUntilIndexesReady();
+
+		// do completion
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2();
+		ICompilationUnit cu= getCompilationUnit("P1", "src", "p11", "Y11.java");
+
+		String str = cu.getSource();
+		String completeBehind = "X";
+		int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+		cu.codeComplete(cursorLocation, requestor);
+
+		assertResults(
+			"",
+			requestor.getResults());
+	} finally {
+		this.deleteProject("AccessRestrictions");
+		this.deleteProject("P1");
+		JavaCore.setOptions(oldOptions);
+	}
+}
+public void testBug575562_AccessRestrictionCheck_DISABLED() throws Exception {
+	Hashtable oldOptions = JavaCore.getOptions();
+	try {
+		Hashtable options = new Hashtable(oldOptions);
+		options.put(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, JavaCore.ERROR);
+		options.put(JavaCore.CODEASSIST_FORBIDDEN_REFERENCE_CHECK, JavaCore.DISABLED);
+		options.put(JavaCore.CODEASSIST_DISCOURAGED_REFERENCE_CHECK, JavaCore.DISABLED);
+		JavaCore.setOptions(options);
+		setUpJavaProject("AccessRestrictions", "1.4", false);
+		createJavaProject(
+				"P1",
+				new String[] {"src"},
+				new String[] {"JCL_LIB", "/AccessRestrictions/lib.jar"},
+				new String[][]{{}, {}},
+				new String[][]{{}, {"**/*"}},
+				null/*no project*/,
+				null/*no inclusion pattern*/,
+				null/*no exclusion pattern*/,
+				null/*no exported project*/,
+				"bin",
+				null/*no source outputs*/,
+				null/*no inclusion pattern*/,
+				null/*no exclusion pattern*/,
+				"1.4");
+		this.createFolder("/P1/src/p11");
+		this.createFile(
+				"/P1/src/p11/Y11.java",
+				"package p11;\n"+
+				"public class YY11 {\n"+
+				"  void foo() {\n"+
+				"    X\n"+
+				"  }\n"+
+				"}");
+		waitUntilIndexesReady();
+
+		// do completion
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2();
+		ICompilationUnit cu= getCompilationUnit("P1", "src", "p11", "Y11.java");
+
+		String str = cu.getSource();
+		String completeBehind = "X";
+		int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+		cu.codeComplete(cursorLocation, requestor);
+
+		assertResults(
+			"X[TYPE_REF]{p.X, p, Lp.X;, null, 50}",
+			requestor.getResults());
+	} finally {
+		this.deleteProject("AccessRestrictions");
+		this.deleteProject("P1");
+		JavaCore.setOptions(oldOptions);
+	}
+}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests3.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests3.java
index d6b8dd6..a18ac50 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests3.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests3.java
@@ -1190,6 +1190,7 @@
 		int cursorLocation = str.indexOf(completeAfter) + completeAfter.length();
 		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
 		assertResults(
+			"class[FIELD_REF]{class, null, Ljava.lang.Class<LThread;>;, class, null, 51}\n" +
 			"sleep[METHOD_REF]{sleep(), LThread;, (I)V, sleep, (millis), 51}",
 			requestor.getResults());
 	} finally {
@@ -1255,6 +1256,7 @@
 			"serialVersionUID[FIELD_REF]{serialVersionUID, Ljava.lang.Enum<LThread$State;>;, J, serialVersionUID, null, 49}\n" +
 			"BLOCKED[FIELD_REF]{BLOCKED, LThread$State;, LThread$State;, BLOCKED, null, 51}\n" +
 			"NEW[FIELD_REF]{NEW, LThread$State;, LThread$State;, NEW, null, 51}\n" +
+			"class[FIELD_REF]{class, null, Ljava.lang.Class<LThread$State;>;, class, null, 51}\n" +
 			"valueOf[METHOD_REF]{valueOf(), LThread$State;, (Ljava.lang.String;)LThread$State;, valueOf, (arg0), 51}\n" +
 			"values[METHOD_REF]{values(), LThread$State;, ()[LThread$State;, values, null, 51}",
 			requestor.getResults());
@@ -1262,4 +1264,259 @@
 		deleteProject("P");
 	}
 }
+public void testBug575631_comment0() throws Exception {
+	try {
+		createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11");
+		this.workingCopies = new ICompilationUnit[1];
+		this.workingCopies[0] = getWorkingCopy(
+			"/P/src/ContentAssist.java",
+			"import java.util.Calendar;\n" +
+			"class ZoneId {}\n" +
+			"class LocalDateTime {\n" +
+			"	static LocalDateTime now() { return null; }\n" +
+			"	static LocalDateTime now(ZoneId id) { return null; }\n" +
+			"}\n" +
+			"public class ContentAssist {\n" +
+			"	public static void staticMethod() {\n" +
+			"		if (true) {\n" +
+			"			LocalDateTime.now\n" +
+			"			Calendar calendar = Calendar.getInstance();\n" +
+			"		}\n" +
+			"	}\n" +
+			"}\n");
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+		String str = this.workingCopies[0].getSource();
+		String completeAfter = "LocalDateTime.now";
+		int cursorLocation = str.indexOf(completeAfter) + completeAfter.length();
+		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+		assertResults(
+			"now[METHOD_REF]{now(), LLocalDateTime;, ()LLocalDateTime;, now, null, 55}\n" +
+			"now[METHOD_REF]{now(), LLocalDateTime;, (LZoneId;)LLocalDateTime;, now, (id), 55}",
+			requestor.getResults());
+	} finally {
+		deleteProject("P");
+	}
+}
+public void testBug575631_comment1a() throws Exception {
+	try {
+		createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11");
+		this.workingCopies = new ICompilationUnit[1];
+		this.workingCopies[0] = getWorkingCopy(
+			"/P/src/missing_proposals_for_static_fields_and_methods.java",
+			"\n" +
+			"class System {\n" +
+			"	static Object out;\n" +
+			"	static Object getEnv() { return null; }\n" +
+			"}\n" +
+			"class missing_proposals_for_static_fields_and_methods {\n" +
+			"	void sample(String foo) {\n" +
+			"		if (foo == null) {\n" +
+			"			System. // <- missing: \"out\", \"getenv()\", etc. (similar to bug 574267)\n" +
+			"			System.out.println();\n" +
+			"		}\n" +
+			"		System. // <- here content assist works fine\n" +
+			"	}\n" +
+			"}\n");
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+		String str = this.workingCopies[0].getSource();
+		String completeAfter = "System.";
+		int cursorLocation = str.indexOf(completeAfter) + completeAfter.length();
+		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+		assertResults(
+			"class[FIELD_REF]{class, null, Ljava.lang.Class<LSystem;>;, class, null, 51}\n" +
+			"getEnv[METHOD_REF]{getEnv(), LSystem;, ()Ljava.lang.Object;, getEnv, null, 51}\n" +
+			"out[FIELD_REF]{out, LSystem;, Ljava.lang.Object;, out, null, 51}",
+			requestor.getResults());
+	} finally {
+		deleteProject("P");
+	}
+}
+public void testBug575631_comment1b() throws Exception {
+	try {
+		createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11");
+		this.workingCopies = new ICompilationUnit[1];
+		this.workingCopies[0] = getWorkingCopy(
+			"/P/src/missing_proposals_for_static_fields_and_methods.java",
+			"\n" +
+			"class System {\n" +
+			"	static Object out;\n" +
+			"	static Object getEnv() { return null; }\n" +
+			"}\n" +
+			"class missing_proposals_for_static_fields_and_methods {\n" +
+			"	void sample(String foo) {\n" +
+			"		if (foo == null) {\n" +
+			"			sample(\"\");\n" +
+			"		} else {\n" +
+			"			System. // <- missing: \"out\", \"getenv()\", etc. (similar to bug 574215)\n" +
+			"			System.out.println();\n" +
+			"		}\n" +
+			"		System. // <- here content assist works fine\n" +
+			"	}\n" +
+			"}\n");
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+		String str = this.workingCopies[0].getSource();
+		String completeAfter = "System.";
+		int cursorLocation = str.indexOf(completeAfter) + completeAfter.length();
+		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+		assertResults(
+			"class[FIELD_REF]{class, null, Ljava.lang.Class<LSystem;>;, class, null, 51}\n" +
+			"getEnv[METHOD_REF]{getEnv(), LSystem;, ()Ljava.lang.Object;, getEnv, null, 51}\n" +
+			"out[FIELD_REF]{out, LSystem;, Ljava.lang.Object;, out, null, 51}",
+			requestor.getResults());
+	} finally {
+		deleteProject("P");
+	}
+}
+public void testBug575631_comment3() throws Exception {
+	try {
+		createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11");
+		this.workingCopies = new ICompilationUnit[1];
+		this.workingCopies[0] = getWorkingCopy(
+			"/P/src/X.java",
+			"class OutputStream {\n" +
+			"	void println() {}\n" +
+			"}\n" +
+			"interface Runnable { void run(); }\n" +
+			"class System {\n" +
+			"	static OutputStream out;\n" +
+			"	static Object getEnv() { return null; }\n" +
+			"}\n" +
+			"class X {\n" +
+			"	void foo() {\n" +
+			"		Runnable r = () -> {\n" +
+			"			System.out.\n" +
+			"			System.out.println();\n" +
+			"		};\n" +
+			"	}\n" +
+			"}\n");
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+		String str = this.workingCopies[0].getSource();
+		String completeAfter = "System.out.";
+		int cursorLocation = str.indexOf(completeAfter) + completeAfter.length();
+		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+		assertResults(
+			"clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 60}\n" +
+			"equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 60}\n" +
+			"finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 60}\n" +
+			"getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<+Ljava.lang.Object;>;, getClass, null, 60}\n" +
+			"hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 60}\n" +
+			"notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 60}\n" +
+			"notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 60}\n" +
+			"println[METHOD_REF]{println(), LOutputStream;, ()V, println, null, 60}\n" +
+			"toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 60}\n" +
+			"wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 60}\n" +
+			"wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 60}\n" +
+			"wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 60}",
+			requestor.getResults());
+	} finally {
+		deleteProject("P");
+	}
+}
+public void testBug575631_comment3b() throws Exception {
+	// method invocation inside lambda in field initializer
+	try {
+		createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11");
+		this.workingCopies = new ICompilationUnit[1];
+		this.workingCopies[0] = getWorkingCopy(
+			"/P/src/X.java",
+			"class OutputStream {\n" +
+			"	void println() {}\n" +
+			"}\n" +
+			"interface Runnable { void run(); }\n" +
+			"class System {\n" +
+			"	static OutputStream out;\n" +
+			"	static Object getEnv() { return null; }\n" +
+			"}\n" +
+			"class X {\n" +
+			"	Runnable r = () -> {\n" +
+			"		System.out.\n" +
+			"		System.out.println();\n" +
+			"	};\n" +
+			"}\n");
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+		String str = this.workingCopies[0].getSource();
+		String completeAfter = "System.out.";
+		int cursorLocation = str.indexOf(completeAfter) + completeAfter.length();
+		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+		assertResults(
+			"clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 60}\n" +
+			"equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 60}\n" +
+			"finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 60}\n" +
+			"getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<+Ljava.lang.Object;>;, getClass, null, 60}\n" +
+			"hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 60}\n" +
+			"notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 60}\n" +
+			"notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 60}\n" +
+			"println[METHOD_REF]{println(), LOutputStream;, ()V, println, null, 60}\n" +
+			"toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 60}\n" +
+			"wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 60}\n" +
+			"wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 60}\n" +
+			"wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 60}",
+			requestor.getResults());
+	} finally {
+		deleteProject("P");
+	}
+}
+public void testBug575631_comment3c() throws Exception {
+	// variable declaration in lambda in field initializer
+	try {
+		createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11");
+		this.workingCopies = new ICompilationUnit[1];
+		this.workingCopies[0] = getWorkingCopy(
+			"/P/src/X.java",
+			"class OutputStream {\n" +
+			"	void println() {}\n" +
+			"}\n" +
+			"interface Consumer { void consume(int); }\n" +
+			"class Number{}\n" +
+			"class System {\n" +
+			"	static OutputStream out;\n" +
+			"	static Object getEnv() { return null; }\n" +
+			"}\n" +
+			"class X {\n" +
+			"	Consumer r = (int number) -> {\n" +
+			"		Number \n" +
+			"		System.out.println();\n" +
+			"	};\n" +
+			"}\n");
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+		String str = this.workingCopies[0].getSource();
+		String completeAfter = "Number ";
+		int cursorLocation = str.indexOf(completeAfter) + completeAfter.length();
+		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+		assertResults(
+			"number[VARIABLE_DECLARATION]{number, null, LNumber;, number, null, 48}", // FIXME: should be number2 => https://bugs.eclipse.org/576781
+			requestor.getResults());
+	} finally {
+		deleteProject("P");
+	}
+}
+public void testBug575631_comment3d() throws Exception {
+	// first of two arguments in method invocation in lambda in field initializer
+	// overloads should be selected by the existing second argument
+	// no separating ',' yet.
+	try {
+		createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11");
+		this.workingCopies = new ICompilationUnit[1];
+		this.workingCopies[0] = getWorkingCopy(
+			"/P/src/X.java",
+			"interface BiConsumer { void consume(int,boolean); }\n" +
+			"class X {\n" +
+			"	BiConsumer r = (int number, boolean bool) -> {\n" +
+			"		bar( number);\n" +
+			"	};\n" +
+			"	void bar(int i, String s) {}\n" +
+			"	void bar(boolean b, int j) {}\n" +
+			"}\n");
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+		String str = this.workingCopies[0].getSource();
+		String completeAfter = "bar(";
+		int cursorLocation = str.indexOf(completeAfter) + completeAfter.length();
+		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+		assertResults(
+			"bar[METHOD_REF]{, LX;, (ZI)V, bar, (b, j), 56}", // select overload with int as 2nd arg
+			requestor.getResults());
+	} finally {
+		deleteProject("P");
+	}
+}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTestsRequestor2.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTestsRequestor2.java
index 072cbe5..48a203c 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTestsRequestor2.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTestsRequestor2.java
@@ -438,6 +438,9 @@
 			case CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION :
 				buffer.append("ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION"); //$NON-NLS-1$
 				break;
+			case CompletionProposal.LAMBDA_EXPRESSION :
+				buffer.append("LAMBDA_EXPRESSION"); //$NON-NLS-1$
+				break;
 			default :
 				buffer.append("PROPOSAL"); //$NON-NLS-1$
 				break;
@@ -709,4 +712,8 @@
 		}
 		return null;
 	}
+
+	public char[][] getExpectedTypesSignatures() {
+		return this.context.getExpectedTypesSignatures();
+	}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java
index a3962cb..44b9319 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java
@@ -11609,6 +11609,7 @@
 	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
 
 	assertResults(
+			"hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" +
 			"get[METHOD_REF]{, Ltest.util.List<Ljava.lang.String;>;, (I)Ljava.lang.String;, get, (i), " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_EXACT_NAME + R_UNQUALIFIED + R_NON_RESTRICTED) + "}",
 			requestor.getResults());
 }
@@ -11676,6 +11677,7 @@
 	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
 
 	assertResults(
+			"hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" +
 			"abs[METHOD_REF]{, Ltest.util.Math;, (I)I, abs, (i), " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_EXACT_NAME + R_UNQUALIFIED + R_NON_RESTRICTED) + "}",
 			requestor.getResults());
 }
@@ -11706,6 +11708,7 @@
 	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
 
 	assertResults(
+			"hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" +
 			"abs[METHOD_REF]{, Ltest.util.Math;, (I)I, abs, (i), " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_EXACT_NAME + R_UNQUALIFIED + R_NON_RESTRICTED) + "}",
 			requestor.getResults());
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExclusionPatternsTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExclusionPatternsTests.java
index 028b7ad..0222e0d 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExclusionPatternsTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExclusionPatternsTests.java
@@ -435,6 +435,8 @@
 
 	ICompilationUnit cu = getCompilationUnit("/P/src/p/A.java");
 	assertTrue("CU should be on classpath", this.project.isOnClasspath(cu));
+
+	assertResourceOnClasspathEntry(this.project, file, "/P/src");
 }
 /*
  * Ensures that a cu that is excluded is not on the classpath of the project.
@@ -450,6 +452,8 @@
 	);
 	assertTrue("Resource should not be on classpath", !this.project.isOnClasspath(file));
 
+	assertResourceNotOnClasspathEntry(this.project, file);
+
 	ICompilationUnit cu = getCompilationUnit("/P/src/p/A.java");
 	assertTrue("CU should not be on classpath", !this.project.isOnClasspath(cu));
 }
@@ -461,6 +465,8 @@
 	createFolder("/P/src/p");
 	IFile file = createFile("/P/src/p/readme.txt", "");
 	assertTrue("Resource should be on classpath", this.project.isOnClasspath(file));
+
+	assertResourceOnClasspathEntry(this.project, file, "/P/src");
 }
 /*
  * Ensures that a non-java resource that is excluded is not on the classpath of the project.
@@ -470,6 +476,8 @@
 	createFolder("/P/src/p");
 	IFile file = createFile("/P/src/p/readme.txt", "");
 	assertTrue("Resource should not be on classpath", !this.project.isOnClasspath(file));
+
+	assertResourceNotOnClasspathEntry(this.project, file);
 }
 /*
  * Ensures that an excluded nested source folder doesn't appear as a non-java resource of the outer folder.
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/InclusionPatternsTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/InclusionPatternsTests.java
index 5d2c0ba..19c2b40 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/InclusionPatternsTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/InclusionPatternsTests.java
@@ -537,6 +537,8 @@
 	);
 	assertTrue("Resource should not be on classpath", !this.project.isOnClasspath(file));
 
+	assertResourceNotOnClasspathEntry(this.project, file);
+
 	ICompilationUnit cu = getCompilationUnit("/P/src/p/A.java");
 	assertTrue("CU should not be on classpath", !this.project.isOnClasspath(cu));
 }
@@ -554,6 +556,8 @@
 	);
 	assertTrue("Resource should be on classpath", this.project.isOnClasspath(file));
 
+	assertResourceOnClasspathEntry(this.project, file, "/P/src");
+
 	ICompilationUnit cu = getCompilationUnit("/P/src/p/A.java");
 	assertTrue("CU should be on classpath", this.project.isOnClasspath(cu));
 }
@@ -565,6 +569,7 @@
 	createFolder("/P/src/p");
 	IFile file = createFile("/P/src/p/readme.txt", "");
 	assertTrue("Resource should not be on classpath", !this.project.isOnClasspath(file));
+	assertResourceNotOnClasspathEntry(this.project, file);
 }
 /*
  * Ensures that a non-java resource that is included is on the classpath of the project.
@@ -574,6 +579,7 @@
 	createFolder("/P/src/p");
 	IFile file = createFile("/P/src/p/readme.txt", "");
 	assertTrue("Resource should be on classpath", this.project.isOnClasspath(file));
+	assertResourceOnClasspathEntry(this.project, file, "/P/src");
 }
 /*
  * Ensures that moving a folder that contains an included package reports the correct delta.
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests16.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests16.java
index bdc8992..1eb2ece 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests16.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests16.java
@@ -193,4 +193,62 @@
 		deleteProject(p);
 	}
 }
+public void testBug576448_001() throws Exception {
+	if (!isJRE16)
+		return;
+	IJavaProject p = createJava16Project("p");
+	createFolder("/p/src/a");
+	try {
+		createFile("p/src/a/X.java",
+				"package a;\n"+
+				"import a.Interface.NestedInterface;\n"+
+				"import a.Interface.NestedInterface2;\n"+
+				"\n"+
+				"public record X(String someString, NestedInterface someInterface) implements NestedInterface2 {\n"+
+				" public X(NestedInterface someInterface) {\n"+
+				"   this(null, someInterface); // <- error here\n"+
+				" }\n"+
+				" public X(String someString, NestedInterface someInterface) {\n"+
+				"   this.someString = someString;\n"+
+				"   this.someInterface = someInterface;\n"+
+				" }\n"+
+				" public static void main(String[] args) {\n"+
+				"   System.out.println(\"hello\");\n"+
+				" }\n"+
+				"}");
+		createFile("p/src/a/Interface.java",
+				"package a;\n"+
+				"public interface Interface {\n"+
+				" interface NestedInterface {\n"+
+				" }\n"+
+				" interface NestedInterface2 {\n"+
+				"   String someString();\n"+
+				"   NestedInterface someInterface();\n"+
+				"   static NestedInterface2 create(String s, NestedInterface n) {\n"+
+				"     return new X(s, n);\n"+
+				"   }\n"+
+				"   static NestedInterface2 create(NestedInterface n) {\n"+
+				"     return new X(n);\n"+
+				"   }\n"+
+				" }\n"+
+				"}");
+
+		p.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
+		IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE);
+		assertMarkers("markers in p",
+				"",
+				markers);
+
+		this.workingCopy = getCompilationUnit("p/src/X.java").getWorkingCopy(this.wcOwner, null);
+		this.problemRequestor.initialize(this.workingCopy.getSource().toCharArray());
+		this.workingCopy.reconcile(JLS_LATEST, true, this.wcOwner, null);
+		assertProblems("Expecting no problems",
+				"----------\n" +
+				"----------\n",
+				this.problemRequestor);
+		this.workingCopy.discardWorkingCopy();
+	} finally {
+		deleteProject(p);
+	}
+}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SubwordCompletionTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SubwordCompletionTests.java
index d377304..a6e8043 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SubwordCompletionTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SubwordCompletionTests.java
@@ -897,6 +897,7 @@
 	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
 
 	assertResults(
+			"toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED ) +  "}\n" +
 			"put[METHOD_REF]{, Ljava.util.Map<Ljava.lang.String;Ljava.lang.String;>;, (Ljava.lang.String;Ljava.lang.String;)Ljava.lang.String;, put, (key, value), " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED ) + "}",
 			requestor.getResults());
 }
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/.classpath b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/.classpath
new file mode 100644
index 0000000..e22b721
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+    <classpathentry kind="lib" path="lib/bug565512.jar" rootpath=""/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+    <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/.project b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/.project
new file mode 100644
index 0000000..aa4d11d
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>JavaSearchBug565512lib</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/bin/dummy/Dummy.class b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/bin/dummy/Dummy.class
new file mode 100644
index 0000000..4faf163
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/bin/dummy/Dummy.class
Binary files differ
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/lib/bug565512.jar b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/lib/bug565512.jar
new file mode 100644
index 0000000..005540a
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/lib/bug565512.jar
Binary files differ
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/src/dummy/Dummy.java b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/src/dummy/Dummy.java
new file mode 100644
index 0000000..44de296
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/src/dummy/Dummy.java
@@ -0,0 +1,12 @@
+package dummy;
+
+public class Dummy {
+
+	// note that "String" implicit means "java.lang.String":
+	public static void main(String[] args) {
+	}
+//	// TODO: somehow it makes a difference during search if "java.lang.String" is used in the class:
+	//bug 576306
+	void any(java.lang.String[] a) {
+	}
+}
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/.classpath b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/.classpath
new file mode 100644
index 0000000..f31482b
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="lib" path="lib/bug565512Module.jar"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+		<attributes>
+			<attribute name="module" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/.project b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/.project
new file mode 100644
index 0000000..62020e3
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>JavaSearchBug565512libModule</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..a58ebdc
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,15 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=11
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=11
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/bin/dummy/Dummy.class b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/bin/dummy/Dummy.class
new file mode 100644
index 0000000..4faf163
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/bin/dummy/Dummy.class
Binary files differ
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/lib/bug565512Module.jar b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/lib/bug565512Module.jar
new file mode 100644
index 0000000..655a51e
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/lib/bug565512Module.jar
Binary files differ
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/src/dummy/Dummy.java b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/src/dummy/Dummy.java
new file mode 100644
index 0000000..44de296
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/src/dummy/Dummy.java
@@ -0,0 +1,12 @@
+package dummy;
+
+public class Dummy {
+
+	// note that "String" implicit means "java.lang.String":
+	public static void main(String[] args) {
+	}
+//	// TODO: somehow it makes a difference during search if "java.lang.String" is used in the class:
+	//bug 576306
+	void any(java.lang.String[] a) {
+	}
+}
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.classpath b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.classpath
new file mode 100644
index 0000000..33c97a3
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+		<attributes>
+			<attribute name="module" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.gitignore b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.project b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.project
new file mode 100644
index 0000000..69f9b49
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.project
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>bug565512</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+	<linkedResources>
+		<link>
+			<name>src</name>
+			<type>2</type>
+			<locationURI>$%257BPARENT-2-PROJECT_LOC%257D/JavaSearchBug565512src/src</locationURI>
+		</link>
+	</linkedResources>
+</projectDescription>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..cd8d089
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,15 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=11
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=11
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/export bug565512.jar.jardesc b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/export bug565512.jar.jardesc
new file mode 100644
index 0000000..355ce27
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/export bug565512.jar.jardesc
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?>
+<jardesc>
+    <jar path="org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512lib/lib/bug565512.jar"/>
+    <options buildIfNeeded="true" compress="true" descriptionLocation="/bug565512/export bug565512.jar.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="true" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
+    <storedRefactorings deprecationInfo="true" structuralOnly="false"/>
+    <selectedProjects/>
+    <manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
+        <sealing sealJar="false">
+            <packagesToSeal/>
+            <packagesToUnSeal/>
+        </sealing>
+    </manifest>
+    <selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
+        <javaElement handleIdentifier="=bug565512/src"/>
+    </selectedElements>
+</jardesc>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/export bug565512WithSrc.jar.jardesc b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/export bug565512WithSrc.jar.jardesc
new file mode 100644
index 0000000..b408f36
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512/export bug565512WithSrc.jar.jardesc
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?>
+<jardesc>
+    <jar path="org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/lib/bug565512WithSrc.jar"/>
+    <options buildIfNeeded="true" compress="true" descriptionLocation="/bug565512/export bug565512WithSrc.jar.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="true" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
+    <storedRefactorings deprecationInfo="true" structuralOnly="false"/>
+    <selectedProjects/>
+    <manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
+        <sealing sealJar="false">
+            <packagesToSeal/>
+            <packagesToUnSeal/>
+        </sealing>
+    </manifest>
+    <selectedElements exportClassFiles="true" exportJavaFiles="true" exportOutputFolder="false">
+        <javaElement handleIdentifier="=bug565512/src"/>
+    </selectedElements>
+</jardesc>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.classpath b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.classpath
new file mode 100644
index 0000000..19d5e94
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="src2"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+		<attributes>
+			<attribute name="module" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.gitignore b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.project b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.project
new file mode 100644
index 0000000..9998c14
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.project
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>bug565512Module</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+	<linkedResources>
+		<link>
+			<name>src</name>
+			<type>2</type>
+			<locationURI>$%257BPARENT-2-PROJECT_LOC%257D/JavaSearchBug565512src/src</locationURI>
+		</link>
+	</linkedResources>
+</projectDescription>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..a58ebdc
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,15 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=11
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=11
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/export bug565512Module.jar.jardesc b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/export bug565512Module.jar.jardesc
new file mode 100644
index 0000000..0b83220
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/export bug565512Module.jar.jardesc
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?>
+<jardesc>
+    <jar path="org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libModule/lib/bug565512Module.jar"/>
+    <options buildIfNeeded="true" compress="true" descriptionLocation="/bug565512Module/export bug565512Module.jar.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="true" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
+    <storedRefactorings deprecationInfo="true" structuralOnly="false"/>
+    <selectedProjects/>
+    <manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
+        <sealing sealJar="false">
+            <packagesToSeal/>
+            <packagesToUnSeal/>
+        </sealing>
+    </manifest>
+    <selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
+        <javaElement handleIdentifier="=bug565512Module/src"/>
+        <javaElement handleIdentifier="=bug565512Module/src2"/>
+    </selectedElements>
+</jardesc>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/src2/module-info.java b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/src2/module-info.java
new file mode 100644
index 0000000..9b2b4c5
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libProjects/bug565512Module/src2/module-info.java
@@ -0,0 +1,2 @@
+module bug565512 {
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/.classpath b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/.classpath
new file mode 100644
index 0000000..bcb39f4
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+    <classpathentry kind="lib" path="lib/bug565512WithSrc.jar" rootpath=""/>
+    <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/.project b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/.project
new file mode 100644
index 0000000..0a1dbac
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>JavaSearchBug565512libWithSrc</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/bin/dummy/Dummy.class b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/bin/dummy/Dummy.class
new file mode 100644
index 0000000..4faf163
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/bin/dummy/Dummy.class
Binary files differ
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/lib/bug565512WithSrc.jar b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/lib/bug565512WithSrc.jar
new file mode 100644
index 0000000..c22b9ce
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/lib/bug565512WithSrc.jar
Binary files differ
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/src/dummy/Dummy.java b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/src/dummy/Dummy.java
new file mode 100644
index 0000000..44de296
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512libWithSrc/src/dummy/Dummy.java
@@ -0,0 +1,12 @@
+package dummy;
+
+public class Dummy {
+
+	// note that "String" implicit means "java.lang.String":
+	public static void main(String[] args) {
+	}
+//	// TODO: somehow it makes a difference during search if "java.lang.String" is used in the class:
+	//bug 576306
+	void any(java.lang.String[] a) {
+	}
+}
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/.classpath b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/.classpath
new file mode 100644
index 0000000..fb50116
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/.gitignore b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/.gitignore
new file mode 100644
index 0000000..ae3c172
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/.gitignore
@@ -0,0 +1 @@
+/bin/
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/.project b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/.project
new file mode 100644
index 0000000..f9323b6
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>JavaSearchBug565512src</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/$/$/$.java b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/$/$/$.java
new file mode 100644
index 0000000..2bbd1c2
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/$/$/$.java
@@ -0,0 +1,48 @@
+package $.$;
+
+class $ {               	// $.$.$
+	int $;              	// $.$.$.$
+	void $() {}         	// $.$.$.$()
+	class $I {          	// $.$.$.$I
+		int $;          	// $.$.$.$I.$
+		void $() {}     	// $.$.$.$I.$()
+		class $II {	    	// $.$.$.$I.$II
+			int $;      	// $.$.$.$I.$II.$
+			void $() {} 	// $.$.$.$I.$II.$()
+		}
+	}
+	static class $SI {  	// $.$.$.$SI
+		int $;          	// $.$.$.$SI.$
+		void $() {}			// $.$.$.$SI.$()
+		static class $SII {	// $.$.$.$SI.$SII
+			int $;        	// $.$.$.$SI.$SII.$
+			void $() {}		// $.$.$.$SI.$SII.$()
+		}
+	}
+
+	class $E extends $ {    	// $.$.$.$E
+		int $;          		// $.$.$.$E.$
+		void $() {}     		// $.$.$.$E.$()
+		class $EE extends $E {	// $.$.$.$E.$EE
+			int $;      		// $.$.$.$E.$EE.$
+			void $() {} 		// $.$.$.$E.$EE.$()
+		}
+	}
+	static class $SE extends $ {// $.$.$.$SE
+		int $;          		// $.$.$.$SE.$
+		void $() {}     		// $.$.$.$E.$()
+		static class $SEE extends $SE {	// $.$.$.$SE.$SEE
+			int $;        		// $.$.$.$SE.$SEE.$
+			void $() {			// $.$.$.$SE.$SEE.$()
+				new $().$();
+				(new $().new $I()).$();
+				(new $().new $I().new $II()).$();
+				new $SI().$();
+				(new $SI.$SII()).$();
+				new $SE().$();
+				(new $SE.$SEE()).$();
+			}
+		}
+	}
+
+}
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512/Class565512.java b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512/Class565512.java
new file mode 100644
index 0000000..8099324
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512/Class565512.java
@@ -0,0 +1,98 @@
+package lib565512;
+
+//this class has references itself
+public class Class565512 {
+	class InnerClass {
+		class InnerClass2 {
+		}
+	}
+
+	static class StaticNestedClass {
+		static class StaticNestedClass2 {
+		}
+	}
+
+	{  class LocalClass {{ class LocalClass2 {}}}
+		new Class565512() {// Anonymous
+			{new Class565512() {// Anonymous2
+				};
+			}
+		};
+	}
+	public static class PublicStaticInnerClass {}
+	private class PrivateInnerClass {}
+	private static class PrivateStaticInnerClass {}
+
+	public class ExtendedClass extends Class565512 {}
+	public class ExtendedInnerClass extends InnerClass {}
+	static class ExtendedPublicStaticInnerClass extends PublicStaticInnerClass {}
+	private class ExtendedPrivateInnerClass extends PrivateInnerClass {}
+	private static class ExtendedPrivateStaticInnerClass extends PrivateStaticInnerClass {}
+
+	void inside(Class565512 c) {
+		class InnerClassInside {
+			class InnerClass2Inside {
+			}
+		}
+
+
+		{  class LocalClassInside {{ class LocalClass2Inside {}}}
+			new Class565512() {// Anonymous
+				{new Class565512() {// Anonymous2
+					};
+				}
+			};
+		}
+	}
+
+	void x() {
+		x();
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+	}
+
+	private static void privateStatic() {
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+	}
+
+	public static void customMain(String[] args) {
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+	}
+
+	public static String stringReturning() {
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+		return null;
+	}
+
+	public static void main(String[] args) {
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+	}
+
+	public static int primitiveMain(int[] args) {
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+		return 0;
+	}
+}
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512/ExtendedClass565512.java b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512/ExtendedClass565512.java
new file mode 100644
index 0000000..26435e6
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512/ExtendedClass565512.java
@@ -0,0 +1,59 @@
+package lib565512;
+
+//this class has references to lib565512.Class565512
+public class ExtendedClass565512 extends Class565512{
+	class InnerClass {}
+	public static class PublicStaticInnerClass {}
+	private class PrivateInnerClass {}
+	private static class PrivateStaticInnerClass {}
+
+	public class ExtendedClass extends Class565512 {}
+	public class ExtendedInnerClass extends Class565512.InnerClass {}
+	static class ExtendedPublicStaticInnerClass extends Class565512.PublicStaticInnerClass {}
+
+	void x() {
+		Class565512 c = new Class565512();
+		c.x();
+		c.main(null);
+		c.customMain(null);
+		c.primitiveMain(null);
+		c.stringReturning();
+	}
+
+	private static void privateStatic() {
+		Class565512.main(null);
+		Class565512.customMain(null);
+		Class565512.primitiveMain(null);
+		Class565512.stringReturning();
+	}
+
+	public static void customMain(String[] args) {
+		Class565512.main(null);
+		Class565512.customMain(null);
+		Class565512.primitiveMain(null);
+		Class565512.stringReturning();
+	}
+
+	public static String stringReturning() {
+		Class565512.main(null);
+		Class565512.customMain(null);
+		Class565512.primitiveMain(null);
+		Class565512.stringReturning();
+		return null;
+	}
+
+	public static void main(String[] args) {
+		Class565512.main(null);
+		Class565512.customMain(null);
+		Class565512.primitiveMain(null);
+		Class565512.stringReturning();
+	}
+
+	public static int primitiveMain(int[] args) {
+		Class565512.main(null);
+		Class565512.customMain(null);
+		Class565512.primitiveMain(null);
+		Class565512.stringReturning();
+		return 0;
+	}
+}
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512wrong/Class565512.java b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512wrong/Class565512.java
new file mode 100644
index 0000000..85ba385
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512wrong/Class565512.java
@@ -0,0 +1,67 @@
+package lib565512wrong;
+
+// this class has no references to lib565512.Class565512
+// but it has the same unqualified name
+public class Class565512 {
+	class InnerClass {}
+	public static class PublicStaticInnerClass {}
+	private class PrivateInnerClass {}
+	private static class PrivateStaticInnerClass {}
+
+	public class ExtendedClass extends Class565512 {}
+	public class ExtendedInnerClass extends InnerClass {}
+	static class ExtendedPublicStaticInnerClass extends PublicStaticInnerClass {}
+	private class ExtendedPrivateInnerClass extends PrivateInnerClass {}
+	private static class ExtendedPrivateStaticInnerClass extends PrivateStaticInnerClass {}
+
+	void x() {
+		x();
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+	}
+
+	private static void privateStatic() {
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+	}
+
+	public static void customMain(String[] args) {
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+	}
+
+	public static String stringReturning() {
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+		return null;
+	}
+
+	public static void main(String[] args) {
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+	}
+
+	public static int primitiveMain(int[] args) {
+		main(null);
+		customMain(null);
+		privateStatic();
+		primitiveMain(null);
+		stringReturning();
+		return 0;
+	}
+}
diff --git a/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512wrong/WrongClass565512.java b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512wrong/WrongClass565512.java
new file mode 100644
index 0000000..7bc2ed7
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/JavaSearchBug565512src/src/lib565512wrong/WrongClass565512.java
@@ -0,0 +1,36 @@
+package lib565512wrong;
+
+// this class has no references to lib565512.Class565512
+// but it has the same member names
+public class WrongClass565512 {
+	class InnerClass {}
+	public static class PublicStaticInnerClass {}
+	private class PrivateInnerClass {}
+	private static class PrivateStaticInnerClass {}
+
+	public class ExtendedClass extends WrongClass565512 {}
+	public class ExtendedInnerClass extends InnerClass {}
+	static class ExtendedPublicStaticInnerClass extends PublicStaticInnerClass {}
+	private class ExtendedPrivateInnerClass extends PrivateInnerClass {}
+	private static class ExtendedPrivateStaticInnerClass extends PrivateStaticInnerClass {}
+
+	void x() {
+	}
+
+	private static void privateStatic() {
+	}
+
+	public static void customMain(String[] args) {
+	}
+
+	public static String stringReturning() {
+		return null;
+	}
+
+	public static void main(String[] args) {
+	}
+
+	public static int primitiveMain(int[] args) {
+		return 0;
+	}
+}
diff --git a/org.eclipse.jdt.core/.settings/.api_filters b/org.eclipse.jdt.core/.settings/.api_filters
index 91eb28a..d0691df 100644
--- a/org.eclipse.jdt.core/.settings/.api_filters
+++ b/org.eclipse.jdt.core/.settings/.api_filters
@@ -730,6 +730,15 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="model/org/eclipse/jdt/core/CompletionProposal.java" type="org.eclipse.jdt.core.CompletionProposal">
+        <filter comment="give way to new constant from JDT" id="388194388">
+            <message_arguments>
+                <message_argument value="org.eclipse.jdt.core.CompletionProposal"/>
+                <message_argument value="OVERRIDE_ROLE_DECLARATION"/>
+                <message_argument value="30"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="model/org/eclipse/jdt/core/Flags.java" type="org.eclipse.jdt.core.Flags">
         <filter comment="Must yield to new AccRecord." id="388194388">
             <message_arguments>
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 265eb0d..f1fa3bf 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
@@ -2199,36 +2199,6 @@
 						continue;
 					}
 				}
-				if (currentArg.equals("-15") || currentArg.equals("-15.0")) { //$NON-NLS-1$ //$NON-NLS-2$
-					if (didSpecifyCompliance) {
-						throw new IllegalArgumentException(
-							this.bind("configure.duplicateCompliance", currentArg)); //$NON-NLS-1$
-					}
-					didSpecifyCompliance = true;
-					this.options.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_15);
-					mode = DEFAULT;
-					continue;
-				}
-				if (currentArg.equals("-16") || currentArg.equals("-16.0")) { //$NON-NLS-1$ //$NON-NLS-2$
-					if (didSpecifyCompliance) {
-						throw new IllegalArgumentException(
-							this.bind("configure.duplicateCompliance", currentArg)); //$NON-NLS-1$
-					}
-					didSpecifyCompliance = true;
-					this.options.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_16);
-					mode = DEFAULT;
-					continue;
-				}
-				if (currentArg.equals("-17") || currentArg.equals("-17.0")) { //$NON-NLS-1$ //$NON-NLS-2$
-					if (didSpecifyCompliance) {
-						throw new IllegalArgumentException(
-							this.bind("configure.duplicateCompliance", currentArg)); //$NON-NLS-1$
-					}
-					didSpecifyCompliance = true;
-					this.options.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_16);
-					mode = DEFAULT;
-					continue;
-				}
 				if (currentArg.equals("-d")) { //$NON-NLS-1$
 					if (this.destinationPath != null) {
 						StringBuilder errorMessage = new StringBuilder();
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
index b665a01..7206e92 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
@@ -17,6 +17,7 @@
  *     Stephan Herrmann - Contribution for
  *								Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec)
  *     Gábor Kövesdán - Contribution for Bug 350000 - [content assist] Include non-prefix matches in auto-complete suggestions
+ *     Microsoft Corporation - Contribution for bug 575562 - improve completion search performance
  *******************************************************************************/
 package org.eclipse.jdt.internal.codeassist;
 
@@ -709,6 +710,7 @@
 	private final static char[] STATIC = "static".toCharArray();  //$NON-NLS-1$
 	private final static char[] ON_DEMAND = ".*".toCharArray();  //$NON-NLS-1$
 	private final static char[] IMPORT_END = ";\n".toCharArray();  //$NON-NLS-1$
+	private final static char[] LAMBDA = "->".toCharArray(); //$NON-NLS-1$
 
 	private final static char[] JAVA_LANG_OBJECT_SIGNATURE =
 		createTypeSignature(CharOperation.concatWith(JAVA_LANG, '.'), OBJECT);
@@ -880,6 +882,15 @@
 
 	private int foundConstructorsCount;
 	private ObjectVector acceptedConstructors;
+	/**
+	 * The strictMatchForExtepectedType used to skip all elements found at <code>findVariablesAndMethods</code>
+	 * which doesn't match the current expected types in the engine in <code>expectedTypes</code>. Since in this mode
+	 * all elements found matches the expected type, the completion proposals will not contains the calculated expected type
+	 * relevance. This is done to keep the overloaded method suggestions always on top in this mode as a fix for
+	 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=575149
+	 *
+	 */
+	private boolean strictMatchForExtepectedType = false;
 
 	/**
 	 * The CompletionEngine is responsible for computing source completions.
@@ -2043,11 +2054,15 @@
 		} else if (astNode instanceof CompletionOnQualifiedNameReference) {
 			completionOnQualifiedNameReference(astNode, enclosingNode, qualifiedBinding, scope, insideTypeAnnotation);
 		} else if (astNode instanceof CompletionOnQualifiedTypeReference) {
-			completionOnQualifiedTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
+			completionOnQualifiedTypeReference(astNode, astNodeParent, enclosingNode, qualifiedBinding, scope);
 		} else if (astNode instanceof CompletionOnMemberAccess) {
 			completionOnMemberAccess(astNode, enclosingNode, qualifiedBinding, scope, insideTypeAnnotation);
 		} else if (astNode instanceof CompletionOnMessageSend) {
 			completionOnMessageSend(astNode, qualifiedBinding, scope);
+			// rebuild the context with newly found expected types so other completion computers can benifit from it.
+			if(this.expectedTypesPtr > -1) {
+				buildContext(astNode, astNodeParent, compilationUnitDeclaration, qualifiedBinding, scope);
+			}
 //{ObjectTeams:	complete rhs method spec in a callout or callin:
 		} else if (astNode instanceof CompletionOnMethodSpec) {
 			completionOnMethodSpec(astNode, qualifiedBinding, scope);
@@ -3305,9 +3320,9 @@
 		CompletionOnMessageSend messageSend = (CompletionOnMessageSend) astNode;
 		TypeBinding[] argTypes = computeTypes(messageSend.arguments);
 		this.completionToken = messageSend.selector;
+		ObjectVector methodsFound = new ObjectVector();
 		if (qualifiedBinding == null) {
 			if (!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
-				ObjectVector methodsFound = new ObjectVector();
 
 				findImplicitMessageSends(this.completionToken, argTypes, scope, messageSend, scope, methodsFound);
 
@@ -3329,7 +3344,7 @@
 				argTypes,
 				(ReferenceBinding)((ReferenceBinding) qualifiedBinding).capture(scope, messageSend.receiver.sourceStart, messageSend.receiver.sourceEnd),
 				scope,
-				new ObjectVector(),
+				methodsFound,
 				false,
 				true,
 				messageSend,
@@ -3345,6 +3360,40 @@
 				-1,
 				-1);
 		}
+
+		findCompletionsForArgumentPosition(methodsFound, argTypes != null ? argTypes.length : 0, scope);
+	}
+
+	private void findCompletionsForArgumentPosition(ObjectVector methodsFound, int completedArgumentLength, Scope scope) {
+		if(methodsFound.size == 0) {
+			return;
+		}
+
+		for(int i = 0; i < methodsFound.size; i++) {
+			MethodBinding method = (MethodBinding) ((Object[])methodsFound.elementAt(i))[0];
+			if(method.parameters.length <= completedArgumentLength) {
+				continue;
+			}
+
+			TypeBinding paramType = method.parameters[completedArgumentLength];
+			addExpectedType(paramType, scope);
+		}
+		this.strictMatchForExtepectedType = true;
+		int filter = this.expectedTypesFilter;
+		this.expectedTypesFilter = SUBTYPE;
+		int start = this.startPosition, end = this.endPosition;
+		int tStart = this.tokenStart, tEnd = this.tokenEnd;
+		try {
+			this.startPosition = this.endPosition = this.tokenStart = this.tokenEnd = this.actualCompletionPosition + 1;
+			findVariablesAndMethods(CharOperation.NO_CHAR, scope, FakeInvocationSite, scope, false, false, false, methodsFound);
+		} finally {
+			this.startPosition = start;
+			this.endPosition = end;
+			this.tokenStart = tStart;
+			this.tokenEnd = tEnd;
+			this.strictMatchForExtepectedType = false;
+			this.expectedTypesFilter = filter;
+		}
 	}
 
 	private void completionOnMessageSendName(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
@@ -3658,8 +3707,15 @@
 		CompletionOnQualifiedNameReference ref =
 			(CompletionOnQualifiedNameReference) astNode;
 		this.completionToken = ref.completionIdentifier;
-		long completionPosition = ref.sourcePositions[ref.sourcePositions.length - 1];
+		internalCompletionOnQualifiedReference(astNode, enclosingNode, qualifiedBinding, scope, insideTypeAnnotation,
+				ref.isInsideAnnotationAttribute, ref, ref.tokens, ref.sourcePositions);
+	}
 
+	/** Unified handling for true QualifiedNameReference and misclassified QualifiedTypeReference. */
+	private void internalCompletionOnQualifiedReference(ASTNode ref, ASTNode enclosingNode, Binding qualifiedBinding, Scope scope,
+			boolean insideTypeAnnotation, boolean isInsideAnnotationAttribute, InvocationSite site, char[][] tokens, long[] sourcePositions)
+	{
+		long completionPosition = sourcePositions[sourcePositions.length - 1];
 		if (qualifiedBinding.problemId() == ProblemReasons.NotFound) {
 			setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
 			// complete field members with missing fields type
@@ -3673,20 +3729,20 @@
 					(this.requestor.isAllowingRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_REF) ||
 							this.requestor.isAllowingRequiredProposals(CompletionProposal.METHOD_REF, CompletionProposal.TYPE_REF) ||
 							this.requestor.isAllowingRequiredProposals(CompletionProposal.TYPE_REF, CompletionProposal.TYPE_REF))) {
-				if(ref.tokens.length == 1) {
-					boolean foundSomeFields = findFieldsAndMethodsFromMissingFieldType(ref.tokens[0], scope, ref, insideTypeAnnotation);
+				if(tokens.length == 1) {
+					boolean foundSomeFields = findFieldsAndMethodsFromMissingFieldType(tokens[0], scope, site, insideTypeAnnotation);
 
 					if (!foundSomeFields) {
 
 						checkCancel();
 
 						findMembersFromMissingType(
-								ref.tokens[0],
-								ref.sourcePositions[0],
+								tokens[0],
+								sourcePositions[0],
 								null,
 								scope,
-								ref,
-								ref.isInsideAnnotationAttribute);
+								site,
+								isInsideAnnotationAttribute);
 					}
 				}
 			}
@@ -3703,7 +3759,7 @@
 						scope,
 						fieldsFound,
 						methodsFound,
-						ref,
+						site,
 						scope,
 						false,
 						false,
@@ -3717,15 +3773,17 @@
 
 				checkCancel();
 
-				findFieldsAndMethodsFromCastedReceiver(
+				if (ref instanceof Expression) {
+					findFieldsAndMethodsFromCastedReceiver(
 						enclosingNode,
 						qualifiedBinding,
 						scope,
 						fieldsFound,
 						methodsFound,
-						ref,
+						site,
 						scope,
-						ref);
+						(Expression) ref);
+				}
 
 			} else if (this.assistNodeInJavadoc == 0 &&
 					(this.requestor.isAllowingRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_REF) ||
@@ -3733,7 +3791,7 @@
 				boolean proposeField = !this.requestor.isIgnored(CompletionProposal.FIELD_REF);
 				boolean proposeMethod = !this.requestor.isIgnored(CompletionProposal.METHOD_REF);
 				if (proposeField || proposeMethod) {
-					if(ref.tokens.length == 1) {
+					if(tokens.length == 1) {
 						if (qualifiedBinding instanceof LocalVariableBinding) {
 							// complete local variable members with missing variables type
 							// class X {
@@ -3746,7 +3804,7 @@
 							findFieldsAndMethodsFromMissingType(
 									localVariableBinding.declaration.type,
 									localVariableBinding.declaringScope,
-									ref,
+									site,
 									scope);
 						} else {
 							// complete field members with missing fields type
@@ -3756,7 +3814,7 @@
 							//     f.|
 							//   }
 							// }
-							findFieldsAndMethodsFromMissingFieldType(ref.tokens[0], scope, ref, insideTypeAnnotation);
+							findFieldsAndMethodsFromMissingFieldType(tokens[0], scope, site, insideTypeAnnotation);
 						}
 
 					}
@@ -3764,7 +3822,6 @@
 			}
 
 		} else if (qualifiedBinding instanceof ReferenceBinding && !(qualifiedBinding instanceof TypeVariableBinding)) {
-			boolean isInsideAnnotationAttribute = ref.isInsideAnnotationAttribute;
 			ReferenceBinding receiverType = (ReferenceBinding) qualifiedBinding;
 			setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
 
@@ -3772,7 +3829,7 @@
 					this.completionToken,
 					receiverType,
 					scope,
-					ref,
+					site,
 					isInsideAnnotationAttribute,
 					null,
 					null,
@@ -3781,7 +3838,7 @@
 
 		} else if (qualifiedBinding instanceof PackageBinding) {
 
-			setSourceRange(astNode.sourceStart, (int) completionPosition);
+			setSourceRange(ref.sourceStart, (int) completionPosition);
 			setTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
 
 			// replace to the end of the completion identifier
@@ -3789,7 +3846,7 @@
 		}
 	}
 
-	private void completionOnQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding,
+	private void completionOnQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, ASTNode enclosingNode, Binding qualifiedBinding,
 			Scope scope) {
 		this.insideQualifiedReference = true;
 
@@ -3807,6 +3864,8 @@
 		this.completionToken = ref.completionIdentifier;
 		long completionPosition = ref.sourcePositions[ref.tokens.length];
 
+		boolean haveTypeProposals = false;
+
 		// get the source positions of the completion identifier
 		if (qualifiedBinding.problemId() == ProblemReasons.NotFound) {
 			setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
@@ -3821,16 +3880,6 @@
 			}
 		} else if (qualifiedBinding instanceof ReferenceBinding && !(qualifiedBinding instanceof TypeVariableBinding)) {
 			ReferenceBinding receiverType = (ReferenceBinding) qualifiedBinding;
-			if (astNodeParent instanceof LocalDeclaration && ref.nextToken == TerminalTokens.TokenNameLPAREN && !this.assistNodeIsConstructor) {
-				// the subsequent '(' makes the interpretation as LocalDeclaration illegal (unless it's "new prefix.token()").
-				// therefore we assume that "name(" (where name is LocalDeclaration.name) is the start of a new statement,
-				// and propose *everything* that can be referenced via the receiverType:
-				findMethods(this.completionToken, null, null, receiverType, scope, new ObjectVector(), true/*onlyStatic*/, false,
-						FakeInvocationSite, scope, false, false, false, null, null, null, false, null, -1, -1);
-				findFields(this.completionToken, receiverType, scope, new ObjectVector(), new ObjectVector(), true/*onlyStatic*/,
-						FakeInvocationSite, scope, false, false, null, null, null, false, null, -1, -1);
-				// fall through to propose member types
-			}
 			if (!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
 				setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
 
@@ -3859,6 +3908,7 @@
 					null,
 					null,
 					false);
+				haveTypeProposals = typesFound.size() > 0;
 			}
 		} else if (qualifiedBinding instanceof PackageBinding) {
 
@@ -3867,24 +3917,17 @@
 			// replace to the end of the completion identifier
 			findTypesAndSubpackages(this.completionToken, (PackageBinding) qualifiedBinding, scope);
 		}
-		ASTNode parentNode = this.parser.assistNodeParent;
-		if (ref.tokens.length > 0 && parentNode instanceof LocalDeclaration && ((LocalDeclaration) parentNode).type == ref) {
-			// additionally check if 'prefix.' should be interpreted as a variable receiver rather then part of a type reference:
-			Binding variable = scope.getBinding(ref.tokens[0], Binding.VARIABLE, FakeInvocationSite, true);
-			lookupViaVariable: if (variable instanceof VariableBinding) {
-				TypeBinding receiverType = ((VariableBinding) variable).type;
-				int len = ref.tokens.length;
-				for (int i=1; i<len; i++) {
-					// lookup subsequent fields in 'prefix.q.r.'
-					if (!(receiverType instanceof ReferenceBinding && receiverType.isValidBinding()))
-						break lookupViaVariable;
-					FieldBinding field = scope.getField(receiverType, ref.tokens[i], FakeInvocationSite);
-					if (!field.isValidBinding())
-						break lookupViaVariable;
-					receiverType = field.type;
-				}
-				if (receiverType instanceof ReferenceBinding && receiverType.isValidBinding()) {
-					findFieldsAndMethods(this.completionToken, receiverType, scope, new ObjectVector(), new ObjectVector(), FakeInvocationSite, scope, false, false, null, null, null, false, null, ref.sourceStart, (int)ref.sourcePositions[0]);
+		// alternatively interpret tokens in a misclassified LocalDeclaration like a QualifiedNameReference:
+		if (astNodeParent instanceof LocalDeclaration && enclosingNode != null) { // enclosingNode == null when called from completionOnProvidesInterfacesQualifiedTypeReference
+			if (scope instanceof BlockScope) {
+				// resolve tokens like it's done in CompletionOnQualifiedNameReference:
+				qualifiedBinding = ((BlockScope) scope).getBinding(ref.tokens, FakeInvocationSite);
+				boolean ignoreType = this.requestor.isIgnored(CompletionProposal.TYPE_REF);
+				try {
+					this.requestor.setIgnored(CompletionProposal.TYPE_REF, haveTypeProposals); // temp ignore types if already proposed above
+					internalCompletionOnQualifiedReference(ref, enclosingNode, qualifiedBinding, scope, false, false, FakeInvocationSite, ref.tokens, ref.sourcePositions);
+				} finally {
+					this.requestor.setIgnored(CompletionProposal.TYPE_REF, ignoreType);
 				}
 			}
 		}
@@ -3892,7 +3935,7 @@
 
 	private void completionOnProvidesInterfacesQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
 		// TODO: Filter the results wrt accessibility and add relevance to the results.
-		completionOnQualifiedTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
+		completionOnQualifiedTypeReference(astNode, astNodeParent, null, qualifiedBinding, scope);
 	}
 
 	private void completionOnProvidesImplementationsQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
@@ -6664,7 +6707,13 @@
 			relevance += computeRelevanceForResolution();
 			relevance += computeRelevanceForInterestingProposal(field);
 			relevance += computeRelevanceForCaseMatching(enumConstantName, field.name);
-			relevance += computeRelevanceForExpectingType(field.type);
+			int computeRelevanceForExpectingType = computeRelevanceForExpectingType(field.type);
+			if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
+				continue;
+			} else if (!this.strictMatchForExtepectedType) {
+				relevance += computeRelevanceForExpectingType;
+			}
+
 			relevance += computeRelevanceForEnumConstant(field.type);
 			relevance += computeRelevanceForQualification(needQualification);
 			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
@@ -7333,7 +7382,13 @@
 			relevance += computeRelevanceForResolution();
 			relevance += computeRelevanceForInterestingProposal(field);
 			relevance += computeRelevanceForCaseMatching(fieldName, field.name);
-			relevance += computeRelevanceForExpectingType(field.type);
+			int computeRelevanceForExpectingType = computeRelevanceForExpectingType(field.type);
+			if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
+				continue;
+			} else if(!this.strictMatchForExtepectedType) {
+				relevance += computeRelevanceForExpectingType;
+			}
+
 			relevance += computeRelevanceForEnumConstant(field.type);
 			relevance += computeRelevanceForStatic(onlyStaticFields, field.isStatic());
 			relevance += computeRelevanceForFinal(this.assistNodeIsInsideCase, field.isFinal());
@@ -7543,7 +7598,7 @@
 		int nextPosition = 0;
 		do {
 			ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
-			if (notInJavadoc && itsInterfaces != Binding.NO_SUPERINTERFACES) {
+			if (notInJavadoc && itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
 				if (interfacesToVisit == null) {
 					interfacesToVisit = itsInterfaces;
 					nextPosition = interfacesToVisit.length;
@@ -8824,6 +8879,7 @@
 					findMembers,
 					getTypesMatchRule(),
 					IJavaSearchConstants.TYPE,
+					false,
 					this,
 					this.monitor);
 			acceptTypes(null);
@@ -9941,7 +9997,12 @@
 			relevance += computeRelevanceForResolution();
 			relevance += computeRelevanceForInterestingProposal();
 			relevance += computeRelevanceForCaseMatching(methodName, method.selector);
-			relevance += computeRelevanceForExpectingType(method.returnType);
+			int computeRelevanceForExpectingType = computeRelevanceForExpectingType(method.returnType);
+			if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
+				continue;
+			} else if(!this.strictMatchForExtepectedType) {
+				relevance += computeRelevanceForExpectingType;
+			}
 			relevance += computeRelevanceForEnumConstant(method.returnType);
 			relevance += computeRelevanceForStatic(onlyStaticMethods, method.isStatic());
 			relevance += computeRelevanceForQualification(prefixRequired);
@@ -10224,7 +10285,13 @@
 				relevance += computeRelevanceForResolution();
 				relevance += computeRelevanceForInterestingProposal();
 				relevance += computeRelevanceForCaseMatching(methodName, method.selector);
-				relevance += computeRelevanceForExpectingType(method.returnType);
+				int computeRelevanceForExpectingType = computeRelevanceForExpectingType(method.returnType);
+				if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
+					continue;
+				} else if(!this.strictMatchForExtepectedType) {
+					relevance += computeRelevanceForExpectingType;
+				}
+
 				relevance += computeRelevanceForEnumConstant(method.returnType);
 				relevance += computeRelevanceForStatic(true, method.isStatic());
 				relevance += computeRelevanceForQualification(true);
@@ -10464,8 +10531,20 @@
 			relevance += computeRelevanceForResolution();
 			relevance += computeRelevanceForInterestingProposal();
 			relevance += computeRelevanceForCaseMatching(methodName, method.selector);
-			relevance += computeRelevanceForExpectingType(method.returnType);
-			relevance += computeRelevanceForEnumConstant(method.returnType);
+			int computeRelevanceForExpectingType = computeRelevanceForExpectingType(method.returnType);
+			if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
+				continue;
+			} else if(!this.strictMatchForExtepectedType) {
+				relevance += computeRelevanceForExpectingType;
+			}
+
+			int computeRelevanceForEnumConstant = computeRelevanceForEnumConstant(method.returnType);
+			if(this.strictMatchForExtepectedType && computeRelevanceForEnumConstant <= 0) {
+				continue;
+			} else if(!this.strictMatchForExtepectedType) {
+				relevance += computeRelevanceForEnumConstant;
+			}
+
 			relevance += computeRelevanceForStatic(true, method.isStatic());
 			relevance += computeRelevanceForQualification(false);
 			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
@@ -10573,7 +10652,9 @@
 					isInterface = receiverType.isInterface();
 				}
 				if (!isInterface) {
-					findKeywords(token, new char[][] { Keywords.THIS, Keywords.SUPER }, true, false);
+					if (hasCompatibleEnclosing(scope, receiverType)) {
+						findKeywords(token, new char[][] { Keywords.THIS, Keywords.SUPER }, true, false);
+					}
 				} else {
 					boolean isEqual = false;
 					char[] enclosingSourceName = null;
@@ -10654,6 +10735,16 @@
 		}
 	}
 
+	private boolean hasCompatibleEnclosing(Scope scope, ReferenceBinding receiverType) {
+		ReferenceBinding enclosing = scope.enclosingSourceType();
+		while (enclosing != null) {
+			if (enclosing.isCompatibleWith(receiverType, scope))
+				return true;
+			enclosing = enclosing.enclosingType();
+		}
+		return false;
+	}
+
 	private void findMembersFromMissingType(
 			final char[] token,
 			final long pos,
@@ -12275,6 +12366,7 @@
 				this.nameEnvironment.findConstructorDeclarations(
 						token,
 						getTypesMatchRule(),
+						false,
 						this,
 						this.monitor);
 				acceptConstructors(scope);
@@ -12310,6 +12402,7 @@
 						proposeAllMemberTypes,
 						getTypesMatchRule(),
 						searchFor,
+						false,
 						this,
 						this.monitor);
 				acceptTypes(scope);
@@ -12478,6 +12571,7 @@
 			this.nameEnvironment.findConstructorDeclarations(
 					qualifiedName,
 					getTypesMatchRule(),
+					false,
 					this,
 					this.monitor);
 			acceptConstructors(scope);
@@ -12503,6 +12597,7 @@
 					false,
 					getTypesMatchRule(),
 					searchFor,
+					false,
 					this,
 					this.monitor);
 			acceptTypes(scope);
@@ -13467,6 +13562,17 @@
 		Scope invocationScope,
 		boolean insideTypeAnnotation,
 		boolean insideAnnotationAttribute) {
+		findVariablesAndMethods(token, scope, invocationSite, invocationScope, insideTypeAnnotation, insideAnnotationAttribute, true, new ObjectVector());
+	}
+	private void findVariablesAndMethods(
+		char[] token,
+		Scope scope,
+		InvocationSite invocationSite,
+		Scope invocationScope,
+		boolean insideTypeAnnotation,
+		boolean insideAnnotationAttribute,
+		boolean canBePrefixed,
+		ObjectVector methodsFound) {
 
 		if (token == null)
 			return;
@@ -13480,7 +13586,6 @@
 
 		ObjectVector localsFound = new ObjectVector();
 		ObjectVector fieldsFound = new ObjectVector();
-		ObjectVector methodsFound = new ObjectVector();
 
 		Scope currentScope = scope;
 
@@ -13563,7 +13668,13 @@
 							relevance += computeRelevanceForResolution();
 							relevance += computeRelevanceForInterestingProposal(local);
 							relevance += computeRelevanceForCaseMatching(token, local.name);
-							relevance += computeRelevanceForExpectingType(local.type);
+							int computeRelevanceForExpectingType = computeRelevanceForExpectingType(local.type);
+							if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
+								continue;
+							} else if(!this.strictMatchForExtepectedType) {
+								relevance += computeRelevanceForExpectingType;
+							}
+
 							relevance += computeRelevanceForEnumConstant(local.type);
 							relevance += computeRelevanceForQualification(false);
 							relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for local variable
@@ -13642,7 +13753,7 @@
 									invocationSite,
 									invocationScope,
 									true,
-									true,
+									canBePrefixed,
 									null,
 									null,
 									null,
@@ -13665,7 +13776,7 @@
 									invocationScope,
 									true,
 									false,
-									true,
+									canBePrefixed,
 									null,
 									null,
 									null,
@@ -13722,6 +13833,8 @@
 					token,
 					invocationScope,
 					fieldsFound);
+
+			findLambdaExpressions(currentScope);
 		}
 	}
 
@@ -14241,6 +14354,9 @@
 			case CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION :
 				buffer.append("ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION"); //$NON-NLS-1$
 				break;
+			case CompletionProposal.LAMBDA_EXPRESSION :
+				buffer.append("LAMBDA_EXPRESSION"); //$NON-NLS-1$
+				break;
 			default :
 				buffer.append("PROPOSAL"); //$NON-NLS-1$
 				break;
@@ -15439,4 +15555,66 @@
 		return completion;
 	}
 // SH}
+
+	private void findLambdaExpressions(Scope scope) {
+		if (this.requestor.isIgnored(CompletionProposal.LAMBDA_EXPRESSION) ||
+				this.compilerOptions.sourceLevel < ClassFileConstants.JDK1_8) {
+			return;
+		}
+
+		if (this.expectedTypesPtr > -1) {
+			for (int i = 0; i <= this.expectedTypesPtr; i++) {
+				TypeBinding type = this.expectedTypes[i];
+
+				if (type.isFunctionalInterface(scope)) {
+					int relevance = computeBaseRelevance();
+					relevance += R_EXACT_EXPECTED_TYPE;
+					relevance += R_ABSTRACT_METHOD;
+					relevance += computeRelevanceForResolution();
+					relevance += computeRelevanceForInterestingProposal();
+					relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
+
+					// we are sure this is not null since we already check for eligibility
+					MethodBinding method = type.getSingleAbstractMethod(scope, true);
+					int length = method.parameters.length;
+					char[][] parameterTypeNames = new char[length][];
+
+					for (int j = 0; j < length; j++) {
+						TypeBinding p = method.parameters[j];
+						parameterTypeNames[j] = p.qualifiedSourceName();
+					}
+					char[][] parameterNames = findMethodParameterNames(method, parameterTypeNames);
+
+					InternalCompletionProposal proposal = createProposal(CompletionProposal.LAMBDA_EXPRESSION,
+							this.actualCompletionPosition);
+					proposal.setBinding(method);
+					proposal.setDeclarationSignature(getSignature(method.declaringClass));
+					proposal.setSignature(getSignature(method));
+					MethodBinding original = method.original();
+					if(original != method) {
+						proposal.setOriginalSignature(getSignature(original));
+					}
+					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
+					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
+					proposal.setRelevance(relevance);
+					proposal.setCompletion(LAMBDA);
+					proposal.setParameterTypeNames(parameterTypeNames);
+					proposal.setFlags(method.modifiers);
+					proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
+					proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
+					proposal.setName(method.selector);
+					proposal.setTypeName(method.returnType.qualifiedSourceName());
+					if (parameterNames != null) {
+						proposal.setParameterNames(parameterNames);
+					}
+
+					this.requestor.accept(proposal);
+					if (DEBUG) {
+						this.printDebug(proposal);
+					}
+				}
+			}
+		}
+	}
+
 }
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java
index cc9befb..39cf53f 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java
@@ -960,6 +960,7 @@
 					break;
 				case METHOD_REF:
 				case METHOD_REF_WITH_CASTED_RECEIVER:
+				case LAMBDA_EXPRESSION:
 					try {
 						this.parameterNames = findMethodParameterNames(
 								this.declarationPackageName,
@@ -1164,6 +1165,9 @@
 			case CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION :
 				buffer.append("ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION"); //$NON-NLS-1$
 				break;
+			case CompletionProposal.LAMBDA_EXPRESSION :
+				buffer.append("LAMBDA_EXPRESSION"); //$NON-NLS-1$
+				break;
 			default :
 				buffer.append("PROPOSAL"); //$NON-NLS-1$
 				break;
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnQualifiedTypeReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnQualifiedTypeReference.java
index 4ab064b..c69faaf 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnQualifiedTypeReference.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnQualifiedTypeReference.java
@@ -40,7 +40,6 @@
 	public char[] completionIdentifier;
 
 	public boolean isConstructorType;
-	public int nextToken;
 
 public CompletionOnQualifiedTypeReference(char[][] previousIdentifiers, char[] completionIdentifier, long[] positions) {
 	this(previousIdentifiers, completionIdentifier, positions, K_TYPE);
@@ -50,10 +49,6 @@
 	this.completionIdentifier = completionIdentifier;
 	this.kind = kind;
 }
-public CompletionOnQualifiedTypeReference(char[][] previousIdentifiers, char[] assistName, long[] positions, int kind, int nextToken) {
-	this(previousIdentifiers, assistName, positions, kind);
-	this.nextToken = nextToken;
-}
 @Override
 public void aboutToResolve(Scope scope) {
 	getTypeBinding(scope);
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java
index 138273f..a2480d0 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java
@@ -21,6 +21,7 @@
 package org.eclipse.jdt.internal.codeassist.complete;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /*
  * Parser able to build specific completion parse nodes, given a cursorLocation.
@@ -4315,11 +4316,22 @@
 	if (token != TerminalTokens.TokenNameEOF && this.scanner.currentPosition > this.cursorLocation) {
 		if (!this.diet || this.dietInt != 0) { // do this also when parsing field initializers:
 			if (this.currentToken == TerminalTokens.TokenNameIdentifier
-					&& this.identifierStack[this.identifierPtr].length == 0
-					&& Scanner.isLiteral(token))
-			{
-				// <emptyAssistIdentifier> <someLiteral> is illegal and most likely the literal should be replaced => discard it now
-				return fetchNextToken();
+					&& this.identifierStack[this.identifierPtr].length == 0) {
+				if (Scanner.isLiteral(token)) {
+					// <emptyAssistIdentifier> <someLiteral> is illegal and most likely the literal should be replaced => discard it now
+					return fetchNextToken();
+				}
+				if (token == TerminalTokens.TokenNameIdentifier) {
+					// empty completion identifier followed by another identifier likely means the 2nd id should start another parse node.
+					// To cleanly separate them find a suitable separator:
+					this.scanner.currentPosition = this.scanner.startPosition; // return to startPosition after this charade
+					if (this.unstackedAct < ERROR_ACTION) {
+						if ("LocalVariableDeclaration".equals(reduce(TokenNameSEMICOLON))) //$NON-NLS-1$
+							return TokenNameSEMICOLON; // send ';' to terminate a local variable declaration
+						if ("ArgumentList".equals(reduce(TokenNameCOMMA))) //$NON-NLS-1$
+							return TokenNameCOMMA; // send ',' to terminate an argument in the list
+					}
+				}
 			}
 		}
 		if (!this.diet) { // only when parsing a method body:
@@ -4338,6 +4350,47 @@
 	}
 	return token;
 }
+/*
+ * Variant of parse() without side effects that stops when another token would need to be fetched.
+ * Returns the name of the last reduced rule, or empty string if nothing reduced, or null if error was detected.
+ */
+String reduce(int token) {
+	int myStackTop = this.stateStackTop;
+	int[] myStack = Arrays.copyOf(this.stack, this.stack.length);
+	int act = this.unstackedAct;
+	String reduceName = ""; //$NON-NLS-1$
+	for (;;) {
+		int stackLength = myStack.length;
+		if (++myStackTop >= stackLength) {
+			System.arraycopy(
+				myStack, 0,
+				myStack = new int[stackLength + StackIncrement], 0,
+				stackLength);
+		}
+		myStack[myStackTop] = act;
+		act = tAction(act, token);
+		if (act == ERROR_ACTION)
+			return null;
+		if (act <= NUM_RULES) { // reduce
+			myStackTop--;
+		} else if (act > ERROR_ACTION) { // shift-reduce
+			return reduceName; // ready to accept the next token
+		} else {
+			if (act < ACCEPT_ACTION) { // shift
+				return reduceName; // ready to accept the next token
+			}
+			return reduceName; // ?
+		}
+		do {
+			reduceName = name[non_terminal_index[lhs[act]]];
+			myStackTop -= (rhs[act] - 1);
+			act = ntAction(myStack[myStackTop], lhs[act]);
+			if (act == ACCEPT_ACTION) {
+				return reduceName;
+			}
+		} while (act <= NUM_RULES);
+	}
+}
 @Override
 protected void consumeToken(int token) {
 	if(this.isFirst) {
@@ -5410,7 +5463,7 @@
 			if (ref != null)
 				return ref;
 			return new CompletionOnQualifiedTypeReference(previousIdentifiers, assistName, positions,
-					CompletionOnQualifiedTypeReference.K_TYPE, this.currentToken);
+					CompletionOnQualifiedTypeReference.K_TYPE);
 	}
 }
 @Override
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 4e56c56..3263a86 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
@@ -2844,6 +2844,8 @@
 	int LocalStaticsIllegalVisibilityModifierForInterfaceLocalType = TypeRelated + 1765;
 	/** @since 3.28 */
 	int IllegalModifierForLocalEnumDeclaration = TypeRelated + 1766;
+	/** @since 3.28 */
+	int ClassExtendFinalRecord = TypeRelated + 1767;
 	/* records - end */
 
 
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java
index e3c3116..d3eb821 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java
@@ -73,10 +73,13 @@
 	FlowContext flowContext,
 	FlowInfo flowInfo) {
 	if (this.constantExpressions != null) {
+		int nullPatternCount = 0;
 		for(int i=0; i < this.constantExpressions.length; i++) {
 			Expression e = this.constantExpressions[i];
+			nullPatternCount +=  e instanceof NullLiteral ? 1 : 0;
 			if (i > 0 && (e instanceof Pattern)) {
-				currentScope.problemReporter().IllegalFallThroughToPattern(e);
+				if (!(i == nullPatternCount && e instanceof TypePattern))
+					currentScope.problemReporter().IllegalFallThroughToPattern(e);
 			}
 			analyseConstantExpression(currentScope, flowContext, flowInfo, e);
 		}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
index 727128f..2124bb5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
@@ -974,7 +974,7 @@
 			this.constructorCall = null;
 		} else if (sourceType.isRecord() &&
 				!(this instanceof CompactConstructorDeclaration) && // compact constr should be marked as canonical?
-				(this.binding != null && (this.binding.tagBits & TagBits.IsCanonicalConstructor) == 0) &&
+				(this.binding != null && !this.binding.isCanonicalConstructor()) &&
 				this.constructorCall.accessMode != ExplicitConstructorCall.This) {
 			this.scope.problemReporter().recordMissingExplicitConstructorCallInNonCanonicalConstructor(this);
 			this.constructorCall = null;
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 7b7ab4a..9ed8878 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
@@ -396,7 +396,7 @@
 		try {
 			AbstractMethodDeclaration methodDeclaration = methodScope.referenceMethod();
 			if (methodDeclaration != null && methodDeclaration.binding != null
-					&& (methodDeclaration.binding.tagBits & TagBits.IsCanonicalConstructor) != 0) {
+					&& methodDeclaration.binding.isCanonicalConstructor()) {
 				if (!checkAndFlagExplicitConstructorCallInCanonicalConstructor(methodDeclaration, scope))
 					return;
 			}
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 fbe62b7..645515b 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
@@ -700,7 +700,7 @@
 						hasAbstractMethods = hasAbstractMethods || methodBinding.isAbstract();
 						hasNativeMethods = hasNativeMethods || methodBinding.isNative();
 						if (methods[i].isCanonicalConstructor()) {
-							methodBinding.tagBits |= TagBits.IsCanonicalConstructor;
+							methodBinding.extendedTagBits |= ExtendedTagBits.IsCanonicalConstructor;
 						}
 					}
 				}
@@ -1848,7 +1848,11 @@
 			if (!superclass.isClass() && (superclass.tagBits & TagBits.HasMissingType) == 0) {
 				problemReporter().superclassMustBeAClass(sourceType, superclassRef, superclass);
 			} else if (superclass.isFinal()) {
-				problemReporter().classExtendFinalClass(sourceType, superclassRef, superclass);
+				if (superclass.isRecord()) {
+					problemReporter().classExtendFinalRecord(sourceType, superclassRef, superclass);
+				} else {
+					problemReporter().classExtendFinalClass(sourceType, superclassRef, superclass);
+				}
 			} else if ((superclass.tagBits & TagBits.HasDirectWildcard) != 0) {
 				problemReporter().superTypeCannotUseWildcard(sourceType, superclassRef, superclass);
 			} else if (superclass.erasure().id == TypeIds.T_JavaLangEnum) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExtendedTagBits.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExtendedTagBits.java
index 0e96a53..6d87856 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExtendedTagBits.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExtendedTagBits.java
@@ -24,4 +24,9 @@
 	 *  Flag used to identify the annotation jdk.internal.ValueBased
 	 */
 	int AnnotationValueBased = ASTNode.Bit3;
+
+	// Java 16 Records
+	int IsCanonicalConstructor = ASTNode.Bit4; // constructor
+	int isImplicit  = ASTNode.Bit5; // constructor and method
+
 }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
index ee2898a..415b9bf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
@@ -124,6 +124,7 @@
 	public TypeVariableBinding[] typeVariables = Binding.NO_TYPE_VARIABLES;
 	char[] signature;
 	public long tagBits;
+	public int extendedTagBits = 0; // See values in the interface ExtendedTagBits
 	// Used only for constructors
 	protected AnnotationBinding [] typeAnnotations = Binding.NO_ANNOTATIONS;
 
@@ -1256,6 +1257,12 @@
 	return this.selector == TypeConstants.INIT;
 }
 
+/* Answer true if the method is a canonical constructor
+*/
+public final boolean isCanonicalConstructor() {
+	return (this.extendedTagBits & ExtendedTagBits.IsCanonicalConstructor) != 0;
+}
+
 /* Answer true if the receiver is a compact constructor
 */
 public final boolean isCompactConstructor() {
@@ -1300,6 +1307,14 @@
 	return (this.modifiers & ExtraCompilerModifiers.AccImplementing) != 0;
 }
 
+
+/* Answer true if the method is an implicit method - only for records
+*/
+public final boolean isImplicit() {
+	return (this.extendedTagBits & ExtendedTagBits.isImplicit) != 0;
+}
+
+
 /*
  * Answer true if the receiver is a "public static void main(String[])" method
  */
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java
index d6245f5..585a287 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java
@@ -543,8 +543,9 @@
 }
 private void checkAndSetRecordCanonicalConsAndMethods(AbstractMethodDeclaration am) {
 	if (am.binding != null && (am.bits & ASTNode.IsImplicit) != 0) {
-		am.binding.tagBits |= TagBits.isImplicit;
-		am.binding.tagBits |= (am.bits & ASTNode.IsCanonicalConstructor) != 0 ? TagBits.IsCanonicalConstructor : 0;
+		am.binding.extendedTagBits |= ExtendedTagBits.isImplicit;
+		if ((am.bits & ASTNode.IsCanonicalConstructor) != 0)
+			am.binding.extendedTagBits |= ExtendedTagBits.IsCanonicalConstructor;
 	}
 }
 
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
index b84adc6..dc347d6 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
@@ -1795,7 +1795,7 @@
 		if (method != null && method.isValidBinding() && method.isVarargs()) {
 			TypeBinding elementType = method.parameters[method.parameters.length - 1].leafComponentType();
 			if (elementType instanceof ReferenceBinding) {
-				if (!((ReferenceBinding) elementType).canBeSeenBy(this)) {
+				if (!((ReferenceBinding) elementType).erasure().canBeSeenBy(this)) {
 					return new ProblemMethodBinding(method, method.selector, invocationSite.genericTypeArguments(), ProblemReasons.VarargsElementTypeNotVisible);
 				}
 			}
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 25a4ad1..bd46612 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
@@ -1184,8 +1184,7 @@
 	if (this.isRecordDeclaration &&  getImplicitCanonicalConstructor() == -1) {
 		MethodBinding explicitCanon = null;
 		for (MethodBinding m : methodBindings) {
-			if (m.isCompactConstructor()
-					|| (m.tagBits & TagBits.IsCanonicalConstructor) != 0) {
+			if (m.isCompactConstructor() || m.isCanonicalConstructor()) {
 				explicitCanon = m;
 				break;
 			}
@@ -2756,7 +2755,7 @@
 	if (this.methods != null && this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK14) {
 		for (int i = 0, l = this.methods.length; i < l; ++i) {
 			MethodBinding method = this.methods[i];
-			if ((method.tagBits & TagBits.IsCanonicalConstructor ) != 0 && (method.tagBits & TagBits.isImplicit) != 0)
+			if (method.isCanonicalConstructor() && method.isImplicit())
 				return i;
 		}
 	}
@@ -2769,7 +2768,7 @@
 	for (MethodBinding method : this.methods) {
 		if (!method.isConstructor())
 			continue;
-		if ((method.tagBits & TagBits.isImplicit) != 0) {
+		if (method.isImplicit()) {
 			continue;
 		}
 		if (method.parameters.length != nRecordComponents)
@@ -2798,7 +2797,7 @@
 			MethodBinding method = resolvedMethods[i];
 			if (method == null || !CharOperation.equals(method.selector, name))
 				continue;
-			if ((method.tagBits & TagBits.isImplicit) != 0 || method instanceof SyntheticMethodBinding)
+			if (method.isImplicit() || method instanceof SyntheticMethodBinding)
 				return i;
 		}
 	}
@@ -3141,7 +3140,7 @@
 	if (explicitCanonicalConstructor.thrownExceptions != null && explicitCanonicalConstructor.thrownExceptions.length > 0)
 		this.scope.problemReporter().recordCanonicalConstructorHasThrowsClause(methodDecl);
 	checkCanonicalConstructorParameterNames(explicitCanonicalConstructor, methodDecl);
-	explicitCanonicalConstructor.tagBits |= TagBits.IsCanonicalConstructor;
+	explicitCanonicalConstructor.extendedTagBits |= ExtendedTagBits.IsCanonicalConstructor;
 //	checkAndFlagExplicitConstructorCallInCanonicalConstructor(methodDecl);
 	return explicitCanonicalConstructor;
 }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java
index ec33a91..6099069 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java
@@ -552,7 +552,8 @@
 		if (this.declaringClass.isStrictfp())
 			this.modifiers |= ClassFileConstants.AccStrictfp;
 		this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
-		this.tagBits |= (TagBits.IsCanonicalConstructor | TagBits.isImplicit);
+		this.extendedTagBits |= ExtendedTagBits.IsCanonicalConstructor;
+		this.extendedTagBits |= ExtendedTagBits.isImplicit;
 		this.parameters = rcb.length == 0 ? Binding.NO_PARAMETERS : new TypeBinding[rcb.length];
 		for (int i = 0; i < rcb.length; i++) this.parameters[i] = TypeBinding.VOID; // placeholder
 		this.selector = TypeConstants.INIT;
@@ -560,7 +561,6 @@
 		this.purpose = SyntheticMethodBinding.RecordCanonicalConstructor;
 		this.thrownExceptions = Binding.NO_EXCEPTIONS;
 		this.declaringClass = declaringSourceType;
-		this.tagBits |= TagBits.IsCanonicalConstructor;
 		this.index = nextSmbIndex();
 	}
 	public SyntheticMethodBinding(ReferenceBinding declaringClass, RecordComponentBinding rcb, int index) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java
index 283739d..d830603 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java
@@ -70,10 +70,6 @@
 	long MultiCatchParameter = ASTNode.Bit13; // local
 	long IsResource = ASTNode.Bit14; // local
 
-	// for java 14 Records Canonical constructor (preview)
-	long IsCanonicalConstructor = ASTNode.Bit12; // constructor
-	long isImplicit  = ASTNode.Bit13; // constructor and method
-
 	// have implicit null annotations been collected (inherited(?) & default)?
 	long IsNullnessKnown = ASTNode.Bit13; // method
 
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
index 866b004..9bda423 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
@@ -33,6 +33,7 @@
 import org.eclipse.jdt.internal.compiler.impl.JavaFeature;
 import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
 import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
+import org.eclipse.jdt.internal.compiler.util.CharDeduplication;
 import org.eclipse.jdt.internal.compiler.util.Util;
 
 /**
@@ -337,41 +338,6 @@
 	public static final String INVALID_UNDERSCORE = "Invalid_Underscore"; //$NON-NLS-1$
 	public static final String UNDERSCORES_IN_LITERALS_NOT_BELOW_17 = "Underscores_In_Literals_Not_Below_17"; //$NON-NLS-1$
 
-	//----------------optimized identifier managment------------------
-	static final char[] charArray_a = new char[] {'a'},
-		charArray_b = new char[] {'b'},
-		charArray_c = new char[] {'c'},
-		charArray_d = new char[] {'d'},
-		charArray_e = new char[] {'e'},
-		charArray_f = new char[] {'f'},
-		charArray_g = new char[] {'g'},
-		charArray_h = new char[] {'h'},
-		charArray_i = new char[] {'i'},
-		charArray_j = new char[] {'j'},
-		charArray_k = new char[] {'k'},
-		charArray_l = new char[] {'l'},
-		charArray_m = new char[] {'m'},
-		charArray_n = new char[] {'n'},
-		charArray_o = new char[] {'o'},
-		charArray_p = new char[] {'p'},
-		charArray_q = new char[] {'q'},
-		charArray_r = new char[] {'r'},
-		charArray_s = new char[] {'s'},
-		charArray_t = new char[] {'t'},
-		charArray_u = new char[] {'u'},
-		charArray_v = new char[] {'v'},
-		charArray_w = new char[] {'w'},
-		charArray_x = new char[] {'x'},
-		charArray_y = new char[] {'y'},
-		charArray_z = new char[] {'z'};
-
-	static final char[] initCharArray =
-		new char[] {'\u0000', '\u0000', '\u0000', '\u0000', '\u0000', '\u0000'};
-	static final int TableSize = 30, InternalTableSize = 6; //30*6 =210 entries
-
-	public static final int OptimizedLength = 6;
-	public /*static*/ final char[][][][] charArray_length =
-		new char[OptimizedLength - 1][TableSize][InternalTableSize][];
 	// support for detecting non-externalized string literals
 	public static final char[] TAG_PREFIX= "//$NON-NLS-".toCharArray(); //$NON-NLS-1$
 	public static final int TAG_PREFIX_LENGTH= TAG_PREFIX.length;
@@ -392,20 +358,6 @@
 	// generic support
 	public boolean returnOnlyGreater = false;
 
-	/*static*/ {
-		for (int i = 0; i < OptimizedLength - 1; i++) {
-			for (int j = 0; j < TableSize; j++) {
-				for (int k = 0; k < InternalTableSize; k++) {
-					this.charArray_length[i][j][k] = initCharArray;
-				}
-			}
-		}
-	}
-	/*static*/ int newEntry2 = 0,
-		newEntry3 = 0,
-		newEntry4 = 0,
-		newEntry5 = 0,
-		newEntry6 = 0;
 	public boolean insideRecovery = false;
 	int lookBack[] = new int[2]; // fall back to spring forward.
 	protected int nextToken = TokenNameNotAToken; // allows for one token push back, only the most recent token can be reliably ungotten.
@@ -432,6 +384,8 @@
 	//Java 15 - first _ keyword appears
 	Map<String, Integer> _Keywords = null;
 
+	private CharDeduplication deduplication = CharDeduplication.getThreadLocalInstance();
+
 public Scanner() {
 	this(false /*comment*/, false /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3 /*sourceLevel*/, null/*taskTag*/, null/*taskPriorities*/, true /*taskCaseSensitive*/);
 }
@@ -696,24 +650,9 @@
 	}
 	int length = this.currentPosition - this.startPosition;
 	if (length == this.eofPosition) return this.source;
-	switch (length) { // see OptimizedLength
-		case 1 :
-			return optimizedCurrentTokenSource1();
-		case 2 :
-			return optimizedCurrentTokenSource2();
-		case 3 :
-			return optimizedCurrentTokenSource3();
-		case 4 :
-			return optimizedCurrentTokenSource4();
-		case 5 :
-			return optimizedCurrentTokenSource5();
-		case 6 :
-			return optimizedCurrentTokenSource6();
-	}
-	char[] result = new char[length];
-	System.arraycopy(this.source, this.startPosition, result, 0, length);
-	return result;
+	return this.deduplication.sharedCopyOfRange(this.source, this.startPosition, this.currentPosition);
 }
+
 public int getCurrentTokenEndPosition(){
 	return this.currentPosition - 1;
 }
@@ -958,6 +897,9 @@
 				case 's' :
 					result.append(' ');
 					break;
+				case '"':
+					result.append('"');
+					break;
 				case 'b' :
 					result.append('\b');
 					break;
@@ -2446,12 +2388,17 @@
 					case 'n' :
 					case 'r' :
 					case 'f' :
+					case 's' :
 						break outer;
 					case '\n' :
 					case '\r' :
 						this.currentCharacter = '\\';
 						this.currentPosition++;
 						break;
+					case '\"' :
+						this.currentPosition++;
+						this.currentCharacter = this.source[this.currentPosition++];
+						continue;
 					case '\\' :
 						this.currentPosition++;
 						break;
@@ -2709,7 +2656,7 @@
 										pushLineSeparator();
 										//$FALL-THROUGH$
 									default:
-										if (this.currentCharacter == '\\' && this.source[this.currentPosition++] == '"') {
+										if (this.currentCharacter == '\\' && this.source[this.currentPosition] == '"') {
 											this.currentPosition++;
 										}
 										this.currentCharacter = this.source[this.currentPosition++];
@@ -3391,241 +3338,6 @@
 	return CharOperation.isWhitespace(this.currentCharacter);
 }
 
-final char[] optimizedCurrentTokenSource1() {
-	//return always the same char[] build only once
-
-	//optimization at no speed cost of 99.5 % of the singleCharIdentifier
-	char charOne = this.source[this.startPosition];
-	switch (charOne) {
-		case 'a' :
-			return charArray_a;
-		case 'b' :
-			return charArray_b;
-		case 'c' :
-			return charArray_c;
-		case 'd' :
-			return charArray_d;
-		case 'e' :
-			return charArray_e;
-		case 'f' :
-			return charArray_f;
-		case 'g' :
-			return charArray_g;
-		case 'h' :
-			return charArray_h;
-		case 'i' :
-			return charArray_i;
-		case 'j' :
-			return charArray_j;
-		case 'k' :
-			return charArray_k;
-		case 'l' :
-			return charArray_l;
-		case 'm' :
-			return charArray_m;
-		case 'n' :
-			return charArray_n;
-		case 'o' :
-			return charArray_o;
-		case 'p' :
-			return charArray_p;
-		case 'q' :
-			return charArray_q;
-		case 'r' :
-			return charArray_r;
-		case 's' :
-			return charArray_s;
-		case 't' :
-			return charArray_t;
-		case 'u' :
-			return charArray_u;
-		case 'v' :
-			return charArray_v;
-		case 'w' :
-			return charArray_w;
-		case 'x' :
-			return charArray_x;
-		case 'y' :
-			return charArray_y;
-		case 'z' :
-			return charArray_z;
-		default :
-			return new char[] {charOne};
-	}
-}
-final char[] optimizedCurrentTokenSource2() {
-	//try to return the same char[] build only once
-
-	char[] src = this.source;
-	int start = this.startPosition;
-	char c0 , c1;
-	int hash = (((c0=src[start]) << 6) + (c1=src[start+1])) % TableSize;
-	char[][] table = this.charArray_length[0][hash];
-	int i = this.newEntry2;
-	while (++i < InternalTableSize) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0]) && (c1 == charArray[1]))
-			return charArray;
-	}
-	//---------other side---------
-	i = -1;
-	int max = this.newEntry2;
-	while (++i <= max) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0]) && (c1 == charArray[1]))
-			return charArray;
-	}
-	//--------add the entry-------
-	if (++max >= InternalTableSize) max = 0;
-	char[] r;
-	System.arraycopy(src, start, r= new char[2], 0, 2);
-	//newIdentCount++;
-	return table[this.newEntry2 = max] = r; //(r = new char[] {c0, c1});
-}
-final char[] optimizedCurrentTokenSource3() {
-	//try to return the same char[] build only once
-
-	char[] src = this.source;
-	int start = this.startPosition;
-	char c0, c1=src[start+1], c2;
-	int hash = (((c0=src[start])<< 6) + (c2=src[start+2])) % TableSize;
-//	int hash = ((c0 << 12) + (c1<< 6) + c2) % TableSize;
-	char[][] table = this.charArray_length[1][hash];
-	int i = this.newEntry3;
-	while (++i < InternalTableSize) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]))
-			return charArray;
-	}
-	//---------other side---------
-	i = -1;
-	int max = this.newEntry3;
-	while (++i <= max) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]))
-			return charArray;
-	}
-	//--------add the entry-------
-	if (++max >= InternalTableSize) max = 0;
-	char[] r;
-	System.arraycopy(src, start, r= new char[3], 0, 3);
-	//newIdentCount++;
-	return table[this.newEntry3 = max] = r; //(r = new char[] {c0, c1, c2});
-}
-final char[] optimizedCurrentTokenSource4() {
-	//try to return the same char[] build only once
-
-	char[] src = this.source;
-	int start = this.startPosition;
-	char c0, c1 = src[start+1], c2, c3 = src[start+3];
-	int hash = (((c0=src[start]) << 6) + (c2=src[start+2])) % TableSize;
-//	int hash = (int) (((((long) c0) << 18) + (c1 << 12) + (c2 << 6) + c3) % TableSize);
-	char[][] table = this.charArray_length[2][hash];
-	int i = this.newEntry4;
-	while (++i < InternalTableSize) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3]))
-			return charArray;
-	}
-	//---------other side---------
-	i = -1;
-	int max = this.newEntry4;
-	while (++i <= max) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3]))
-			return charArray;
-	}
-	//--------add the entry-------
-	if (++max >= InternalTableSize) max = 0;
-	char[] r;
-	System.arraycopy(src, start, r= new char[4], 0, 4);
-	//newIdentCount++;
-	return table[this.newEntry4 = max] = r; //(r = new char[] {c0, c1, c2, c3});
-}
-final char[] optimizedCurrentTokenSource5() {
-	//try to return the same char[] build only once
-
-	char[] src = this.source;
-	int start = this.startPosition;
-	char c0, c1 = src[start+1], c2, c3 = src[start+3], c4;
-	int hash = (((c0=src[start]) << 12) +((c2=src[start+2]) << 6) + (c4=src[start+4])) % TableSize;
-//	int hash = (int) (((((long) c0) << 24) + (((long) c1) << 18) + (c2 << 12) + (c3 << 6) + c4) % TableSize);
-	char[][] table = this.charArray_length[3][hash];
-	int i = this.newEntry5;
-	while (++i < InternalTableSize) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3])
-			&& (c4 == charArray[4]))
-			return charArray;
-	}
-	//---------other side---------
-	i = -1;
-	int max = this.newEntry5;
-	while (++i <= max) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3])
-			&& (c4 == charArray[4]))
-			return charArray;
-	}
-	//--------add the entry-------
-	if (++max >= InternalTableSize) max = 0;
-	char[] r;
-	System.arraycopy(src, start, r= new char[5], 0, 5);
-	//newIdentCount++;
-	return table[this.newEntry5 = max] = r; //(r = new char[] {c0, c1, c2, c3, c4});
-}
-final char[] optimizedCurrentTokenSource6() {
-	//try to return the same char[] build only once
-
-	char[] src = this.source;
-	int start = this.startPosition;
-	char c0, c1 = src[start+1], c2, c3 = src[start+3], c4, c5 = src[start+5];
-	int hash = (((c0=src[start]) << 12) +((c2=src[start+2]) << 6) + (c4=src[start+4])) % TableSize;
-//	int hash = (int)(((((long) c0) << 32) + (((long) c1) << 24) + (((long) c2) << 18) + (c3 << 12) + (c4 << 6) + c5) % TableSize);
-	char[][] table = this.charArray_length[4][hash];
-	int i = this.newEntry6;
-	while (++i < InternalTableSize) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3])
-			&& (c4 == charArray[4])
-			&& (c5 == charArray[5]))
-			return charArray;
-	}
-	//---------other side---------
-	i = -1;
-	int max = this.newEntry6;
-	while (++i <= max) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3])
-			&& (c4 == charArray[4])
-			&& (c5 == charArray[5]))
-			return charArray;
-	}
-	//--------add the entry-------
-	if (++max >= InternalTableSize) max = 0;
-	char[] r;
-	System.arraycopy(src, start, r= new char[6], 0, 6);
-	//newIdentCount++;
-	return table[this.newEntry6 = max] = r; //(r = new char[] {c0, c1, c2, c3, c4, c5});
-}
 public boolean isInModuleDeclaration() {
 	return this.fakeInModule || this.insideModuleInfo ||
 			(this.activeParser != null ? this.activeParser.isParsingModuleDeclaration() : false);
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 5b55bf9..4dae25d 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
@@ -1846,6 +1846,18 @@
 		superclass.sourceStart,
 		superclass.sourceEnd);
 }
+public void classExtendFinalRecord(SourceTypeBinding type, TypeReference superclass, TypeBinding superTypeBinding) {
+	String name = new String(type.sourceName());
+	String superTypeFullName = new String(superTypeBinding.readableName());
+	String superTypeShortName = new String(superTypeBinding.shortReadableName());
+	if (superTypeShortName.equals(name)) superTypeShortName = superTypeFullName;
+	this.handle(
+		IProblem.ClassExtendFinalRecord,
+		new String[] {superTypeFullName, name},
+		new String[] {superTypeShortName, name},
+		superclass.sourceStart,
+		superclass.sourceEnd);
+}
 public void codeSnippetMissingClass(String missing, int start, int end) {
 	String[] arguments = new String[]{missing};
 	this.handle(
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 74d3628..55daf07 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
@@ -1071,6 +1071,7 @@
 
 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
+1767 = The record {0} cannot be the superclass of {1}; a record is final and cannot be extended
  
 1780 = The pattern variable {0} is not in scope in this location
 1781 = The pattern variable {0} is final and cannot be assigned again
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/CharDeduplication.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/CharDeduplication.java
new file mode 100644
index 0000000..50dc2fa
--- /dev/null
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/CharDeduplication.java
@@ -0,0 +1,284 @@
+/*******************************************************************************
+ * Copyright (c) 2021 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Joerg Kubitz    - threadlocal refactoring, all ASCII chars
+ *                     - (copied content from PublicScanner.java / Scanner.java)
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.util;
+
+import java.lang.ref.SoftReference;
+import java.util.Arrays;
+import java.util.function.Supplier;
+
+public class CharDeduplication {
+
+	// ----- immutable static part (thread safe): ----
+
+	static final char[] ASCII_CHARS[] = new char[128][];
+	static {
+		for (int i = 0; i < ASCII_CHARS.length; i++) {
+			ASCII_CHARS[i] = new char[] { (char) i };
+		}
+	}
+	public static final int TABLE_SIZE = 30; // XXX thats not a prime -> bad for hashing, nor a power of 2 -> expensive
+												// modulo computation
+	public static final int INTERNAL_TABLE_SIZE = 6; // 30*6 =180 entries
+
+	public static final int OPTIMIZED_LENGTH = 6;
+
+	private final static char[] CHAR_ARRAY0 = new char[0];
+
+	/** avoid OOME by additional CharDeduplication memory **/
+	static final class CacheReference<T> {
+		private SoftReference<T> reference;
+		private final Supplier<? extends T> supplier;
+
+		CacheReference(Supplier<? extends T> supplier) {
+			this.supplier = supplier;
+			this.reference = new SoftReference<>(supplier.get());
+		}
+
+		T get() {
+			T referent = this.reference.get();
+			if (referent == null) {
+				referent = this.supplier.get();
+				this.reference = new SoftReference<>(referent);
+			}
+			return referent;
+		}
+	}
+
+	private final static ThreadLocal<CacheReference<CharDeduplication>> mutableCache = ThreadLocal.withInitial(()->new CacheReference<>(CharDeduplication::new));
+
+	private static final char[] optimizedCurrentTokenSource1(char[] source, int startPosition) {
+		// optimization at no speed cost of 99.5 % of the singleCharIdentifier
+		char charOne = source[startPosition];
+		if (charOne < ASCII_CHARS.length) {
+			return ASCII_CHARS[charOne];
+		}
+		return new char[] { charOne };
+	}
+
+	/** @return an instance that is *not* thread safe. To be used in a single thread only. **/
+	public static CharDeduplication getThreadLocalInstance() {
+		return mutableCache.get().get();
+	}
+
+	// ----- mutable non-static part (not thread safe!): ----
+
+	/** single threaded only **/
+	public final char[][][][] charArray_length = new char[OPTIMIZED_LENGTH - 1][TABLE_SIZE][INTERNAL_TABLE_SIZE][];
+
+	int newEntry2 = 0;
+	int newEntry3 = 0;
+	int newEntry4 = 0;
+	int newEntry5 = 0;
+	int newEntry6 = 0;
+
+	private CharDeduplication() {
+		init();
+	}
+
+	private void init() {
+		for (int i = 0; i < OPTIMIZED_LENGTH - 1; i++) {
+			final char[] initCharArray = new char[i + 2];
+			for (int j = 0; j < TABLE_SIZE; j++) {
+				for (int k = 0; k < INTERNAL_TABLE_SIZE; k++) {
+					this.charArray_length[i][j][k] = initCharArray;
+				}
+			}
+		}
+	}
+
+	/** public for test purpose only **/
+	@Deprecated
+	public void reset() {
+		init();
+	}
+
+	/**
+	 * like Arrays.copyOfRange(source, from, to) but returns a cached instance of the former result if
+	 * available
+	 * 
+	 * @param from
+	 *                 start index (inclusive)
+	 * @param to
+	 *                 end index (exclusive)
+	 * @return source[from..to-1]
+	 * @see java.util.Arrays#copyOfRange(char[], int, int)
+	 **/
+	public char[] sharedCopyOfRange(char[] source, int from, int to) {
+		int length = to - from;
+		switch (length) { // see OptimizedLength
+			case 1:
+				return optimizedCurrentTokenSource1(source, from);
+			case 2:
+				return optimizedCurrentTokenSource2(source, from);
+			case 3:
+				return optimizedCurrentTokenSource3(source, from);
+			case 4:
+				return optimizedCurrentTokenSource4(source, from);
+			case 5:
+				return optimizedCurrentTokenSource5(source, from);
+			case 6:
+				return optimizedCurrentTokenSource6(source, from);
+			case 0:
+				return CHAR_ARRAY0;
+		}
+		return Arrays.copyOfRange(source, from, to);
+	}
+
+	private final char[] optimizedCurrentTokenSource2(char[] source, int startPosition) {
+
+		char[] src = source;
+		int start = startPosition;
+		char c0, c1;
+		int hash = (((c0 = src[start]) << 6) + (c1 = src[start + 1])) % TABLE_SIZE;
+		char[][] table = this.charArray_length[0][hash];
+		int i = this.newEntry2;
+		while (++i < INTERNAL_TABLE_SIZE) {
+			char[] charArray = table[i];
+			if ((c0 == charArray[0]) && (c1 == charArray[1]))
+				return charArray;
+		}
+		// ---------other side---------
+		i = -1;
+		int max = this.newEntry2;
+		while (++i <= max) {
+			char[] charArray = table[i];
+			if ((c0 == charArray[0]) && (c1 == charArray[1]))
+				return charArray;
+		}
+		// --------add the entry-------
+		if (++max >= INTERNAL_TABLE_SIZE)
+			max = 0;
+		char[] r;
+		System.arraycopy(src, start, r = new char[2], 0, 2);
+		return table[this.newEntry2 = max] = r;
+	}
+
+	private final char[] optimizedCurrentTokenSource3(char[] source, int startPosition) {
+		char[] src = source;
+		int start = startPosition;
+		char c0, c1 = src[start + 1], c2;
+		int hash = (((c0 = src[start]) << 6) + (c2 = src[start + 2])) % TABLE_SIZE;
+		char[][] table = this.charArray_length[1][hash];
+		int i = this.newEntry3;
+		while (++i < INTERNAL_TABLE_SIZE) {
+			char[] charArray = table[i];
+			if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]))
+				return charArray;
+		}
+		// ---------other side---------
+		i = -1;
+		int max = this.newEntry3;
+		while (++i <= max) {
+			char[] charArray = table[i];
+			if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]))
+				return charArray;
+		}
+		// --------add the entry-------
+		if (++max >= INTERNAL_TABLE_SIZE)
+			max = 0;
+		char[] r;
+		System.arraycopy(src, start, r = new char[3], 0, 3);
+		return table[this.newEntry3 = max] = r;
+	}
+
+	private final char[] optimizedCurrentTokenSource4(char[] source, int startPosition) {
+		char[] src = source;
+		int start = startPosition;
+		char c0, c1 = src[start + 1], c2, c3 = src[start + 3];
+		int hash = (((c0 = src[start]) << 6) + (c2 = src[start + 2])) % TABLE_SIZE;
+		char[][] table = this.charArray_length[2][hash];
+		int i = this.newEntry4;
+		while (++i < INTERNAL_TABLE_SIZE) {
+			char[] charArray = table[i];
+			if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3]))
+				return charArray;
+		}
+		// ---------other side---------
+		i = -1;
+		int max = this.newEntry4;
+		while (++i <= max) {
+			char[] charArray = table[i];
+			if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3]))
+				return charArray;
+		}
+		// --------add the entry-------
+		if (++max >= INTERNAL_TABLE_SIZE)
+			max = 0;
+		char[] r;
+		System.arraycopy(src, start, r = new char[4], 0, 4);
+		return table[this.newEntry4 = max] = r;
+	}
+
+	private final char[] optimizedCurrentTokenSource5(char[] source, int startPosition) {
+		char[] src = source;
+		int start = startPosition;
+		char c0, c1 = src[start + 1], c2, c3 = src[start + 3], c4;
+		int hash = (((c0 = src[start]) << 12) + ((c2 = src[start + 2]) << 6) + (c4 = src[start + 4])) % TABLE_SIZE;
+		char[][] table = this.charArray_length[3][hash];
+		int i = this.newEntry5;
+		while (++i < INTERNAL_TABLE_SIZE) {
+			char[] charArray = table[i];
+			if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3])
+					&& (c4 == charArray[4]))
+				return charArray;
+		}
+		// ---------other side---------
+		i = -1;
+		int max = this.newEntry5;
+		while (++i <= max) {
+			char[] charArray = table[i];
+			if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3])
+					&& (c4 == charArray[4]))
+				return charArray;
+		}
+		// --------add the entry-------
+		if (++max >= INTERNAL_TABLE_SIZE)
+			max = 0;
+		char[] r;
+		System.arraycopy(src, start, r = new char[5], 0, 5);
+		return table[this.newEntry5 = max] = r;
+	}
+
+	private final char[] optimizedCurrentTokenSource6(char[] source, int startPosition) {
+		char[] src = source;
+		int start = startPosition;
+		char c0, c1 = src[start + 1], c2, c3 = src[start + 3], c4, c5 = src[start + 5];
+		int hash = (((c0 = src[start]) << 12) + ((c2 = src[start + 2]) << 6) + (c4 = src[start + 4])) % TABLE_SIZE;
+		char[][] table = this.charArray_length[4][hash];
+		int i = this.newEntry6;
+		while (++i < INTERNAL_TABLE_SIZE) {
+			char[] charArray = table[i];
+			if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3])
+					&& (c4 == charArray[4]) && (c5 == charArray[5]))
+				return charArray;
+		}
+		// ---------other side---------
+		i = -1;
+		int max = this.newEntry6;
+		while (++i <= max) {
+			char[] charArray = table[i];
+			if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]) && (c3 == charArray[3])
+					&& (c4 == charArray[4]) && (c5 == charArray[5]))
+				return charArray;
+		}
+		// --------add the entry-------
+		if (++max >= INTERNAL_TABLE_SIZE)
+			max = 0;
+		char[] r;
+		System.arraycopy(src, start, r = new char[6], 0, 6);
+		return table[this.newEntry6 = max] = r;
+	}
+}
diff --git a/org.eclipse.jdt.core/docker/build-ubuntu-jikespg.sh b/org.eclipse.jdt.core/docker/build-ubuntu-jikespg.sh
new file mode 100644
index 0000000..5bf436d
--- /dev/null
+++ b/org.eclipse.jdt.core/docker/build-ubuntu-jikespg.sh
@@ -0,0 +1,21 @@
+#!/bin/bash -x
+#*******************************************************************************
+# Copyright (c) 2021 IBM Corporation and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+#     Sravan Kumar Lakkimsetti - initial API and implementation
+#*******************************************************************************
+
+set -e
+
+pushd jikespg
+echo "Building Ubuntu based Jikes Parser Generator Ubuntu docker image"
+docker build --pull -t eclipse/platformreleng-ubuntu-gtk3-metacity:jikespg .
+popd
diff --git a/org.eclipse.jdt.core/docker/jikespg/Dockerfile b/org.eclipse.jdt.core/docker/jikespg/Dockerfile
new file mode 100644
index 0000000..63e507b
--- /dev/null
+++ b/org.eclipse.jdt.core/docker/jikespg/Dockerfile
@@ -0,0 +1,31 @@
+FROM ubuntu:20.04
+
+### user name recognition at runtime w/ an arbitrary uid - for OpenShift deployments
+COPY scripts/uid_entrypoint /usr/local/bin/uid_entrypoint
+RUN chmod u+x /usr/local/bin/uid_entrypoint && \
+    chgrp 0 /usr/local/bin/uid_entrypoint && \
+    chmod g=u /usr/local/bin/uid_entrypoint /etc/passwd
+ENTRYPOINT [ "uid_entrypoint" ]
+
+ENV DEBIAN_FRONTEND noninteractive
+RUN apt-get update && apt upgrade -y && apt dist-upgrade -y && apt-get install -y --no-install-recommends \
+      wget \
+      curl \
+      unzip \
+      vim \
+      gcc \
+      g++ \
+      make \
+      git \
+    && rm -rf /var/lib/apt/lists/* && apt autoremove -y
+
+ENV HOME=/home
+ENV DISPLAY :0
+
+RUN git config --global http.sslverify false
+
+RUN mkdir -p ${HOME}/git && cd ${HOME}/git \
+  && git clone -b fixes-combined https://github.com/jikespg/jikespg.git \
+  && cd jikespg/src; make clean; make
+
+USER 10001
diff --git a/org.eclipse.jdt.core/docker/jikespg/scripts/uid_entrypoint b/org.eclipse.jdt.core/docker/jikespg/scripts/uid_entrypoint
new file mode 100644
index 0000000..c44d398
--- /dev/null
+++ b/org.eclipse.jdt.core/docker/jikespg/scripts/uid_entrypoint
@@ -0,0 +1,7 @@
+#!/usr/bin/env sh
+if ! whoami > /dev/null 2>&1; then
+  if [ -w /etc/passwd ]; then
+    echo "${USER_NAME:-default}:x:$(id -u):0:${USER_NAME:-default} user:${HOME}:/sbin/nologin" >> /etc/passwd
+  fi
+fi
+exec "$@"
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/docker/push-ubuntu-jikespg.sh b/org.eclipse.jdt.core/docker/push-ubuntu-jikespg.sh
new file mode 100644
index 0000000..d577562
--- /dev/null
+++ b/org.eclipse.jdt.core/docker/push-ubuntu-jikespg.sh
@@ -0,0 +1,18 @@
+#!/bin/bash -x
+#*******************************************************************************
+# Copyright (c) 2021 IBM Corporation and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+#     Sravan Kumar Lakkimsetti - initial API and implementation
+#*******************************************************************************
+
+set -e
+
+docker push eclipse/platformreleng-ubuntu-gtk3-metacity:jikespg
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 673b448..a453660 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
@@ -92,7 +92,7 @@
 	 */
 	@Override
 	public boolean isCanonicalConstructor() {
-		return ((this.binding.tagBits & TagBits.IsCanonicalConstructor) != 0);
+		return this.binding.isCanonicalConstructor();
 	}
 
 	/**
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/CompletionProposal.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/CompletionProposal.java
index 6edfb3e..55582ef 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/CompletionProposal.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/CompletionProposal.java
@@ -875,6 +875,19 @@
 	 */
 	public static final int MODULE_REF = 29;
 
+	/**
+	/**
+	 * Completion is a lambda expression.
+	 * This kind of completion might occur in a context like
+	 * <code>Consumer consumer = ^</code> and complete it to
+	 * <code>"Consumer consumer = c ->"</code> or in
+	 * <code> "to Consumer consumer = c -> {}"</code>
+	 *
+	 * @see #getKind()
+	 * @since 3.28
+	 */
+	public static final int LAMBDA_EXPRESSION = 30;
+
 //{ObjectTeams: new completion kinds
 	// when forming bitsets of completion kinds we need to fold multiple of our OT constants into the same slot.
 	// therefore we let them differ only by a offsets that are multiples of 32, which will be ignored during bitset operations
@@ -926,7 +939,7 @@
 	/**
 	 * Generate a role type declaration overriding an implicitly inherited role from the super team.
 	 */
-	public static final int OVERRIDE_ROLE_DECLARATION= 30;
+	public static final int OVERRIDE_ROLE_DECLARATION= 31;
 // SH}
 
 	/**
@@ -943,7 +956,7 @@
 	 */
 //{ObjectTeams: include OT completion kinds
 /* orig:
-	protected static final int LAST_KIND = MODULE_REF;
+	protected static final int LAST_KIND = LAMBDA_EXPRESSION;
   :giro */
 	protected static final int LAST_KIND = OVERRIDE_ROLE_DECLARATION;
 // SH}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/Flags.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/Flags.java
index 2f5a5b4..3ec5329 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/Flags.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/Flags.java
@@ -450,8 +450,8 @@
 	 * @param flags the flags
 	 * @return <code>true</code> if the <code>AccSealed</code> flag is included
 	 * @see #AccSealed
-	 * @noreference This method is not intended to be referenced by clients as it is a part of Java preview feature.
-	 */
+	 * @since 3.28
+	 * */
 	public static boolean isSealed(int flags) {
 		return (flags & AccSealed) != 0;
 	}
@@ -462,7 +462,7 @@
 	 * @param flags the flags
 	 * @return <code>true</code> if the <code>AccNonSealed</code> flag is included
 	 * @see #AccNonSealed
-	 * @noreference This method is not intended to be referenced by clients as it is a part of Java preview feature.
+	 * @since 3.28
 	 */
 	public static boolean isNonSealed(int flags) {
 		return (flags & AccNonSealed) != 0;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
index 07f4d3d..a9ccc2a 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
@@ -773,6 +773,18 @@
 	boolean isOnClasspath(IResource resource);
 
 	/**
+	 * Returns the class path entry which contains the given resource and not explicitly excluded using an exclusion
+	 * pattern, or null otherwise.
+	 *
+	 * @param resource
+	 *            the resource which may or may not on one of the class path entries.
+	 * @return the class path entry which contains the given resource, or null, if it's not in any of the classpath
+	 *         entries, or the resource is null.
+	 * @since 3.28
+	 */
+	IClasspathEntry findContainingClasspathEntry(IResource resource);
+
+	/**
 	 * Creates a new evaluation context.
 	 * @return a new evaluation context.
 	 */
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathAccessRule.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathAccessRule.java
index d4cb0fa..f9954f6 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathAccessRule.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathAccessRule.java
@@ -30,7 +30,7 @@
 	}
 
 	public ClasspathAccessRule(char[] pattern, int problemId) {
-		super(pattern, problemId);
+		super(JavaModelManager.getJavaModelManager().intern(pattern), problemId);
 	}
 
 	private static int toProblemId(int kind) {
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
index 459e002..514596a 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
@@ -2824,38 +2824,46 @@
 	 */
 	@Override
 	public boolean isOnClasspath(IResource resource) {
-		IPath exactPath = resource.getFullPath();
-		IPath path = exactPath;
+		return findContainingClasspathEntry(resource) != null;
+	}
 
+	/*
+	 * @see IJavaProject
+	 */
+	@Override
+	public IClasspathEntry findContainingClasspathEntry(IResource resource) {
+		if (resource == null) {
+			return null;
+		}
+		final int resourceType = resource.getType();
 		// ensure that folders are only excluded if all of their children are excluded
-		int resourceType = resource.getType();
-		boolean isFolderPath = resourceType == IResource.FOLDER || resourceType == IResource.PROJECT;
-
+		final boolean isFolderPath = resourceType == IResource.FOLDER || resourceType == IResource.PROJECT;
 		IClasspathEntry[] classpath;
 		try {
 			classpath = this.getResolvedClasspath();
 		} catch(JavaModelException e){
-			return false; // not a Java project
+			return null; // not a Java project
 		}
+		final IPath path = resource.getFullPath();
 		for (int i = 0; i < classpath.length; i++) {
 			IClasspathEntry entry = classpath[i];
 			IPath entryPath = entry.getPath();
-			if (entryPath.equals(exactPath)) { // package fragment roots must match exactly entry pathes (no exclusion there)
-				return true;
+			if (entryPath.equals(path)) { // package fragment roots must match exactly entry pathes (no exclusion there)
+				return entry;
 			}
 			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=276373
 			// When a classpath entry is absolute, convert the resource's relative path to a file system path and compare
 			// e.g - /P/lib/variableLib.jar and /home/P/lib/variableLib.jar when compared should return true
 			if (entryPath.isAbsolute()
-					&& entryPath.equals(ResourcesPlugin.getWorkspace().getRoot().getLocation().append(exactPath))) {
-				return true;
+					&& entryPath.equals(ResourcesPlugin.getWorkspace().getRoot().getLocation().append(path))) {
+				return entry;
 			}
 			if (entryPath.isPrefixOf(path)
 					&& !Util.isExcluded(path, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars(), isFolderPath)) {
-				return true;
+				return entry;
 			}
 		}
-		return false;
+		return null;
 	}
 
 	private boolean isOnClasspathEntry(IPath elementPath, boolean isFolderPath, boolean isPackageFragmentRoot, IClasspathEntry entry) {
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 f093afc..5ac12c4 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
@@ -11,6 +11,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Stephan Herrmann - contribution for bug 337868 - [compiler][model] incomplete support for package-info.java when using SearchableEnvironment
+ *     Microsoft Corporation - contribution for bug 575562 - improve completion search performance
  *******************************************************************************/
 package org.eclipse.jdt.internal.core;
 
@@ -568,6 +569,28 @@
 	 * 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) {
+		findTypes(prefix, findMembers, matchRule, searchFor, true, storage, monitor);
+	}
+
+	/**
+	 * Must be used only by CompletionEngine.
+	 * The progress monitor is used to be able to cancel completion operations
+	 *
+	 * Find the top-level types that are defined
+	 * in the current environment and whose name starts with the
+	 * given prefix. The prefix is a qualified name separated by periods
+	 * or a simple name (ex. java.util.V or V).
+	 *
+	 * The types found are passed to one of the following methods (if additional
+	 * information is known about the types):
+	 *    ISearchRequestor.acceptType(char[][] packageName, char[] typeName)
+	 *    ISearchRequestor.acceptClass(char[][] packageName, char[] typeName, int modifiers)
+	 *    ISearchRequestor.acceptInterface(char[][] packageName, char[] typeName, int modifiers)
+	 *
+	 * This method can not be used to find member types... member
+	 * types are found relative to their enclosing type.
+	 */
+	public void findTypes(char[] prefix, final boolean findMembers, int matchRule, int searchFor, final boolean resolveDocumentName, final ISearchRequestor storage, IProgressMonitor monitor) {
 		long start = -1;
 		if (NameLookup.VERBOSE)
 			start = System.currentTimeMillis();
@@ -671,6 +694,7 @@
 						matchRule, // not case sensitive
 						searchFor,
 						getSearchScope(),
+						resolveDocumentName,
 						typeRequestor,
 						FORCE_IMMEDIATE_SEARCH,
 						progressMonitor);
@@ -693,6 +717,7 @@
 							matchRule,
 							searchFor,
 							getSearchScope(),
+							resolveDocumentName,
 							typeRequestor,
 							FORCE_IMMEDIATE_SEARCH,
 							progressMonitor);
@@ -713,6 +738,7 @@
 						matchRule, // not case sensitive
 						searchFor,
 						getSearchScope(),
+						resolveDocumentName,
 						typeRequestor,
 						CANCEL_IF_NOT_READY_TO_SEARCH,
 						progressMonitor);
@@ -746,7 +772,7 @@
 	 * The constructors found are passed to one of the following methods:
 	 *    ISearchRequestor.acceptConstructor(...)
 	 */
-	public void findConstructorDeclarations(char[] prefix, int matchRule, final ISearchRequestor storage, IProgressMonitor monitor) {
+	public void findConstructorDeclarations(char[] prefix, int matchRule, final boolean resolveDocumentName, final ISearchRequestor storage, IProgressMonitor monitor) {
 		try {
 			final String excludePath;
 			if (this.unitToSkip != null && this.unitToSkip instanceof IJavaElement) {
@@ -879,6 +905,7 @@
 						simpleName,
 						matchRule,
 						getSearchScope(),
+						resolveDocumentName,
 						constructorRequestor,
 						FORCE_IMMEDIATE_SEARCH,
 						progressMonitor);
@@ -889,6 +916,7 @@
 							simpleName,
 							matchRule,
 							getSearchScope(),
+							resolveDocumentName,
 							constructorRequestor,
 							CANCEL_IF_NOT_READY_TO_SEARCH,
 							progressMonitor);
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java
index bd479df..7749d72 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -24,6 +24,7 @@
 import org.eclipse.jdt.internal.compiler.parser.NLSTag;
 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
 import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
+import org.eclipse.jdt.internal.compiler.util.CharDeduplication;
 import org.eclipse.jdt.internal.compiler.util.Util;
 
 public class PublicScanner implements IScanner, ITerminalSymbols {
@@ -214,41 +215,6 @@
 	public static final String INVALID_UNDERSCORE = "Invalid_Underscore"; //$NON-NLS-1$
 	public static final String UNDERSCORES_IN_LITERALS_NOT_BELOW_17 = "Underscores_In_Literals_Not_Below_17"; //$NON-NLS-1$`
 
-	//----------------optimized identifier managment------------------
-	static final char[] charArray_a = new char[] {'a'},
-		charArray_b = new char[] {'b'},
-		charArray_c = new char[] {'c'},
-		charArray_d = new char[] {'d'},
-		charArray_e = new char[] {'e'},
-		charArray_f = new char[] {'f'},
-		charArray_g = new char[] {'g'},
-		charArray_h = new char[] {'h'},
-		charArray_i = new char[] {'i'},
-		charArray_j = new char[] {'j'},
-		charArray_k = new char[] {'k'},
-		charArray_l = new char[] {'l'},
-		charArray_m = new char[] {'m'},
-		charArray_n = new char[] {'n'},
-		charArray_o = new char[] {'o'},
-		charArray_p = new char[] {'p'},
-		charArray_q = new char[] {'q'},
-		charArray_r = new char[] {'r'},
-		charArray_s = new char[] {'s'},
-		charArray_t = new char[] {'t'},
-		charArray_u = new char[] {'u'},
-		charArray_v = new char[] {'v'},
-		charArray_w = new char[] {'w'},
-		charArray_x = new char[] {'x'},
-		charArray_y = new char[] {'y'},
-		charArray_z = new char[] {'z'};
-
-	static final char[] initCharArray =
-		new char[] {'\u0000', '\u0000', '\u0000', '\u0000', '\u0000', '\u0000'};
-	static final int TableSize = 30, InternalTableSize = 6; //30*6 =210 entries
-
-	public static final int OptimizedLength = 6;
-	public /*static*/ final char[][][][] charArray_length =
-		new char[OptimizedLength - 1][TableSize][InternalTableSize][];
 	// support for detecting non-externalized string literals
 	public static final char[] TAG_PREFIX= "//$NON-NLS-".toCharArray(); //$NON-NLS-1$
 	public static final int TAG_PREFIX_LENGTH= TAG_PREFIX.length;
@@ -263,21 +229,6 @@
 	// generic support
 	public boolean returnOnlyGreater = false;
 
-	/*static*/ {
-		for (int i = 0; i < OptimizedLength - 1; i++) {
-			for (int j = 0; j < TableSize; j++) {
-				for (int k = 0; k < InternalTableSize; k++) {
-					this.charArray_length[i][j][k] = initCharArray;
-				}
-			}
-		}
-	}
-
-	/*static*/ int newEntry2 = 0,
-		newEntry3 = 0,
-		newEntry4 = 0,
-		newEntry5 = 0,
-		newEntry6 = 0;
 	public boolean insideRecovery = false;
 
 	public static final int RoundBracket = 0;
@@ -294,6 +245,8 @@
 	// text block support - 13
 	/* package */ int rawStart = -1;
 
+	private CharDeduplication deduplication = CharDeduplication.getThreadLocalInstance();
+
 public PublicScanner() {
 	this(false /*comment*/, false /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3 /*sourceLevel*/, null/*taskTag*/, null/*taskPriorities*/, true /*taskCaseSensitive*/);
 }
@@ -544,24 +497,9 @@
 	}
 	int length = this.currentPosition - this.startPosition;
 	if (length == this.eofPosition) return this.source;
-	switch (length) { // see OptimizedLength
-		case 1 :
-			return optimizedCurrentTokenSource1();
-		case 2 :
-			return optimizedCurrentTokenSource2();
-		case 3 :
-			return optimizedCurrentTokenSource3();
-		case 4 :
-			return optimizedCurrentTokenSource4();
-		case 5 :
-			return optimizedCurrentTokenSource5();
-		case 6 :
-			return optimizedCurrentTokenSource6();
-	}
-	char[] result = new char[length];
-	System.arraycopy(this.source, this.startPosition, result, 0, length);
-	return result;
+	return this.deduplication.sharedCopyOfRange(this.source, this.startPosition, this.currentPosition);
 }
+
 @Override
 public int getCurrentTokenEndPosition(){
 	return this.currentPosition - 1;
@@ -2210,7 +2148,7 @@
 										pushLineSeparator();
 										//$FALL-THROUGH$
 									default:
-										if (this.currentCharacter == '\\' && this.source[this.currentPosition++] == '"') {
+										if (this.currentCharacter == '\\' && this.source[this.currentPosition] == '"') {
 											this.currentPosition++;
 										}
 										this.currentCharacter = this.source[this.currentPosition++];
@@ -2516,242 +2454,6 @@
 	return CharOperation.isWhitespace(this.currentCharacter);
 }
 
-final char[] optimizedCurrentTokenSource1() {
-	//return always the same char[] build only once
-
-	//optimization at no speed cost of 99.5 % of the singleCharIdentifier
-	char charOne = this.source[this.startPosition];
-	switch (charOne) {
-		case 'a' :
-			return charArray_a;
-		case 'b' :
-			return charArray_b;
-		case 'c' :
-			return charArray_c;
-		case 'd' :
-			return charArray_d;
-		case 'e' :
-			return charArray_e;
-		case 'f' :
-			return charArray_f;
-		case 'g' :
-			return charArray_g;
-		case 'h' :
-			return charArray_h;
-		case 'i' :
-			return charArray_i;
-		case 'j' :
-			return charArray_j;
-		case 'k' :
-			return charArray_k;
-		case 'l' :
-			return charArray_l;
-		case 'm' :
-			return charArray_m;
-		case 'n' :
-			return charArray_n;
-		case 'o' :
-			return charArray_o;
-		case 'p' :
-			return charArray_p;
-		case 'q' :
-			return charArray_q;
-		case 'r' :
-			return charArray_r;
-		case 's' :
-			return charArray_s;
-		case 't' :
-			return charArray_t;
-		case 'u' :
-			return charArray_u;
-		case 'v' :
-			return charArray_v;
-		case 'w' :
-			return charArray_w;
-		case 'x' :
-			return charArray_x;
-		case 'y' :
-			return charArray_y;
-		case 'z' :
-			return charArray_z;
-		default :
-			return new char[] {charOne};
-	}
-}
-final char[] optimizedCurrentTokenSource2() {
-	//try to return the same char[] build only once
-
-	char[] src = this.source;
-	int start = this.startPosition;
-	char c0 , c1;
-	int hash = (((c0=src[start]) << 6) + (c1=src[start+1])) % TableSize;
-	char[][] table = this.charArray_length[0][hash];
-	int i = this.newEntry2;
-	while (++i < InternalTableSize) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0]) && (c1 == charArray[1]))
-			return charArray;
-	}
-	//---------other side---------
-	i = -1;
-	int max = this.newEntry2;
-	while (++i <= max) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0]) && (c1 == charArray[1]))
-			return charArray;
-	}
-	//--------add the entry-------
-	if (++max >= InternalTableSize) max = 0;
-	char[] r;
-	System.arraycopy(src, start, r= new char[2], 0, 2);
-	//newIdentCount++;
-	return table[this.newEntry2 = max] = r; //(r = new char[] {c0, c1});
-}
-final char[] optimizedCurrentTokenSource3() {
-	//try to return the same char[] build only once
-
-	char[] src = this.source;
-	int start = this.startPosition;
-	char c0, c1=src[start+1], c2;
-	int hash = (((c0=src[start])<< 6) + (c2=src[start+2])) % TableSize;
-//	int hash = ((c0 << 12) + (c1<< 6) + c2) % TableSize;
-	char[][] table = this.charArray_length[1][hash];
-	int i = this.newEntry3;
-	while (++i < InternalTableSize) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]))
-			return charArray;
-	}
-	//---------other side---------
-	i = -1;
-	int max = this.newEntry3;
-	while (++i <= max) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2]))
-			return charArray;
-	}
-	//--------add the entry-------
-	if (++max >= InternalTableSize) max = 0;
-	char[] r;
-	System.arraycopy(src, start, r= new char[3], 0, 3);
-	//newIdentCount++;
-	return table[this.newEntry3 = max] = r; //(r = new char[] {c0, c1, c2});
-}
-final char[] optimizedCurrentTokenSource4() {
-	//try to return the same char[] build only once
-
-	char[] src = this.source;
-	int start = this.startPosition;
-	char c0, c1 = src[start+1], c2, c3 = src[start+3];
-	int hash = (((c0=src[start]) << 6) + (c2=src[start+2])) % TableSize;
-//	int hash = (int) (((((long) c0) << 18) + (c1 << 12) + (c2 << 6) + c3) % TableSize);
-	char[][] table = this.charArray_length[2][hash];
-	int i = this.newEntry4;
-	while (++i < InternalTableSize) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3]))
-			return charArray;
-	}
-	//---------other side---------
-	i = -1;
-	int max = this.newEntry4;
-	while (++i <= max) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3]))
-			return charArray;
-	}
-	//--------add the entry-------
-	if (++max >= InternalTableSize) max = 0;
-	char[] r;
-	System.arraycopy(src, start, r= new char[4], 0, 4);
-	//newIdentCount++;
-	return table[this.newEntry4 = max] = r; //(r = new char[] {c0, c1, c2, c3});
-}
-final char[] optimizedCurrentTokenSource5() {
-	//try to return the same char[] build only once
-
-	char[] src = this.source;
-	int start = this.startPosition;
-	char c0, c1 = src[start+1], c2, c3 = src[start+3], c4;
-	int hash = (((c0=src[start]) << 12) +((c2=src[start+2]) << 6) + (c4=src[start+4])) % TableSize;
-//	int hash = (int) (((((long) c0) << 24) + (((long) c1) << 18) + (c2 << 12) + (c3 << 6) + c4) % TableSize);
-	char[][] table = this.charArray_length[3][hash];
-	int i = this.newEntry5;
-	while (++i < InternalTableSize) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3])
-			&& (c4 == charArray[4]))
-			return charArray;
-	}
-	//---------other side---------
-	i = -1;
-	int max = this.newEntry5;
-	while (++i <= max) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3])
-			&& (c4 == charArray[4]))
-			return charArray;
-	}
-	//--------add the entry-------
-	if (++max >= InternalTableSize) max = 0;
-	char[] r;
-	System.arraycopy(src, start, r= new char[5], 0, 5);
-	//newIdentCount++;
-	return table[this.newEntry5 = max] = r; //(r = new char[] {c0, c1, c2, c3, c4});
-}
-final char[] optimizedCurrentTokenSource6() {
-	//try to return the same char[] build only once
-
-	char[] src = this.source;
-	int start = this.startPosition;
-	char c0, c1 = src[start+1], c2, c3 = src[start+3], c4, c5 = src[start+5];
-	int hash = (((c0=src[start]) << 12) +((c2=src[start+2]) << 6) + (c4=src[start+4])) % TableSize;
-//	int hash = (int)(((((long) c0) << 32) + (((long) c1) << 24) + (((long) c2) << 18) + (c3 << 12) + (c4 << 6) + c5) % TableSize);
-	char[][] table = this.charArray_length[4][hash];
-	int i = this.newEntry6;
-	while (++i < InternalTableSize) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3])
-			&& (c4 == charArray[4])
-			&& (c5 == charArray[5]))
-			return charArray;
-	}
-	//---------other side---------
-	i = -1;
-	int max = this.newEntry6;
-	while (++i <= max) {
-		char[] charArray = table[i];
-		if ((c0 == charArray[0])
-			&& (c1 == charArray[1])
-			&& (c2 == charArray[2])
-			&& (c3 == charArray[3])
-			&& (c4 == charArray[4])
-			&& (c5 == charArray[5]))
-			return charArray;
-	}
-	//--------add the entry-------
-	if (++max >= InternalTableSize) max = 0;
-	char[] r;
-	System.arraycopy(src, start, r= new char[6], 0, 6);
-	//newIdentCount++;
-	return table[this.newEntry6 = max] = r; //(r = new char[] {c0, c1, c2, c3, c4, c5});
-}
-
 private void parseTags() {
 	int position = 0;
 	final int currentStartPosition = this.startPosition;
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java
index fbf821f..a7dcfe2 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java
@@ -11,6 +11,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Technical University Berlin - extended API and implementation
+ *     Microsoft Corporation - contribution for bug 575562 - improve completion search performance
  *******************************************************************************/
 package org.eclipse.jdt.core.search;
 
@@ -367,6 +368,7 @@
 		this.matchRule &= ~R_PREFIX_MATCH;
 	}
 }
+
 /**
  * @noreference This method is not intended to be referenced by clients.
  * @nooverride This method is not intended to be re-implemented or extended by clients.
@@ -2523,6 +2525,30 @@
  * @nooverride This method is not intended to be re-implemented or extended by clients.
  */
 public void findIndexMatches(Index index, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, IProgressMonitor monitor) throws IOException {
+	findIndexMatches(index, requestor, participant, scope, true, monitor);
+}
+
+/**
+ * Query a given index for matching entries. Assumes the sender has
+ * opened the index and will close when finished.
+ *
+ * This API provides a flag to control whether to skip resolving
+ * document name for the matching entries. If a SearchPattern subclass
+ * has a different implementation of index matching, they have to
+ * override this API to support document name resolving feature.
+ *
+ * @param index the target index to query
+ * @param requestor the search requestor
+ * @param participant the search participant
+ * @param scope the search scope where the search results should be found
+ * @param resolveDocumentName whether to skip the document name resolving
+ *                            for the matching entries
+ * @param monitor a progress monitor
+ *
+ * @noreference This method is not intended to be referenced by clients.
+ * @nooverride This method is not intended to be re-implemented or extended by clients.
+ */
+public void findIndexMatches(Index index, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, boolean resolveDocumentName, IProgressMonitor monitor) throws IOException {
 	if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException();
 	try {
 		index.startQuery();
@@ -2539,10 +2565,15 @@
 			SearchPattern decodedResult = pattern.getBlankPattern();
 			decodedResult.decodeIndexKey(entry.getWord());
 			if (pattern.matchesDecodedKey(decodedResult)) {
-				// TODO (kent) some clients may not need the document names
-				String[] names = entry.getDocumentNames(index);
-				for (int j = 0, n = names.length; j < n; j++)
-					acceptMatch(names[j], containerPath, separator, decodedResult, requestor, participant, scope, monitor);
+				// Since resolve document name is expensive, leave the decision to the search client
+				// to decide whether to do so.
+				if (resolveDocumentName) {
+					String[] names = entry.getDocumentNames(index);
+					for (int j = 0, n = names.length; j < n; j++)
+						acceptMatch(names[j], containerPath, separator, decodedResult, requestor, participant, scope, monitor);
+				} else {
+					acceptMatch("", containerPath, separator, decodedResult, requestor, participant, scope, monitor); //$NON-NLS-1$
+				}
 			}
 		}
 	} finally {
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 f44d749..cce09af 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
@@ -15,6 +15,7 @@
 
 import java.io.*;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.regex.Pattern;
 
@@ -149,11 +150,16 @@
  * If the key is null then all entries in specified categories are returned.
  */
 public EntryResult[] query(char[][] categories, char[] key, int matchRule) throws IOException {
-	if (this.memoryIndex.shouldMerge() && this.monitor.exitReadEnterWrite()) {
+	ReadWriteMonitor readWriteMonitor = this.monitor;
+	if(readWriteMonitor == null) {
+		// index got deleted since acquired
+		return null;
+	}
+	if (this.memoryIndex.shouldMerge() && readWriteMonitor.exitReadEnterWrite()) {
 		try {
 			save();
 		} finally {
-			this.monitor.exitWriteEnterRead();
+			readWriteMonitor.exitWriteEnterRead();
 		}
 	}
 
@@ -240,6 +246,10 @@
 	try {
 		ArrayList<IndexQualifier> qualifiers = new ArrayList<>();
 		for(char[] category : IIndexConstants.META_INDEX_CATEGORIES) {
+			if (this.monitor == null) {
+				// index got deleted since acquired
+				return Collections.emptyList();
+			}
 			EntryResult[] results = query(new char[][] {category}, null,
 					SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE);
 			if(results != null) {
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/BasicSearchEngine.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/BasicSearchEngine.java
index 460e532..8f9de07 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/BasicSearchEngine.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/BasicSearchEngine.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -11,6 +11,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Stephan Herrmann - Contributions for bug 215139 and bug 295894
+ *     Microsoft Corporation - Contribution for bug 575562 - improve completion search performance
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.search;
 
@@ -604,10 +605,37 @@
 	}
 
 	public void searchAllConstructorDeclarations(
+			final char[] packageName,
+			final char[] typeName,
+			final int typeMatchRule,
+			IJavaSearchScope scope,
+			final IRestrictedAccessConstructorRequestor nameRequestor,
+			int waitingPolicy,
+			IProgressMonitor progressMonitor)  throws JavaModelException {
+		searchAllConstructorDeclarations(
+				packageName,
+				typeName,
+				typeMatchRule,
+				scope,
+				true,
+				nameRequestor,
+				waitingPolicy,
+				progressMonitor);
+	}
+
+	/**
+	 *
+	 * Searches for constructor declarations in the given scope.
+	 *
+	 * @param resolveDocumentName used to tell SearchEngine whether to resolve
+	 *                            the document name for each result entry.
+	 */
+	public void searchAllConstructorDeclarations(
 		final char[] packageName,
 		final char[] typeName,
 		final int typeMatchRule,
 		IJavaSearchScope scope,
+		final boolean resolveDocumentName,
 		final IRestrictedAccessConstructorRequestor nameRequestor,
 		int waitingPolicy,
 		IProgressMonitor progressMonitor)  throws JavaModelException {
@@ -730,6 +758,8 @@
 					pattern,
 					getDefaultSearchParticipant(), // Java search only
 					scope,
+					resolveDocumentName,
+					true,
 					searchRequestor),
 				waitingPolicy,
 				subMonitor.split(Math.max(1000-copiesLength, 0)));
@@ -1715,12 +1745,47 @@
 	 * 	for detailed comment
 	 */
 	public void searchAllTypeNames(
+			final char[] packageName,
+			final int packageMatchRule,
+			final char[] typeName,
+			final int typeMatchRule,
+			int searchFor,
+			IJavaSearchScope scope,
+			final IRestrictedAccessTypeRequestor nameRequestor,
+			int waitingPolicy,
+			IProgressMonitor progressMonitor)  throws JavaModelException {
+		searchAllTypeNames(
+				packageName,
+				packageMatchRule,
+				typeName,
+				typeMatchRule,
+				searchFor,
+				scope,
+				true,
+				nameRequestor,
+				waitingPolicy,
+				progressMonitor);
+	}
+
+	/**
+	 * Searches for all top-level types and member types in the given scope.
+	 * The search can be selecting specific types (given a package or a type name
+	 * prefix and match modes).
+	 *
+	 * @param resolveDocumentName used to tell SearchEngine whether to resolve
+	 *                            the document name for each result entry.
+	 *
+	 * @see SearchEngine#searchAllTypeNames(char[], int, char[], int, int, IJavaSearchScope, TypeNameRequestor, int, IProgressMonitor)
+	 * 	for detailed comment
+	 */
+	public void searchAllTypeNames(
 		final char[] packageName,
 		final int packageMatchRule,
 		final char[] typeName,
 		final int typeMatchRule,
 		int searchFor,
 		IJavaSearchScope scope,
+		final boolean resolveDocumentName,
 		final IRestrictedAccessTypeRequestor nameRequestor,
 		int waitingPolicy,
 		IProgressMonitor progressMonitor)  throws JavaModelException {
@@ -1859,12 +1924,15 @@
 			};
 
 			SubMonitor subMonitor = SubMonitor.convert(progressMonitor, Messages.engine_searching, 1000);
+
 			// add type names from indexes
 			indexManager.performConcurrentJob(
 				new PatternSearchJob(
 					pattern,
 					getDefaultSearchParticipant(), // Java search only
 					scope,
+					resolveDocumentName,
+					true,
 					searchRequestor),
 				waitingPolicy,
 				subMonitor.split(Math.max(1000-copiesLength, 0)));
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/PatternSearchJob.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/PatternSearchJob.java
index 32668ee..7054192 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/PatternSearchJob.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/PatternSearchJob.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Microsoft Corporation - contribution for bug 575562 - improve completion search performance
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.search;
 
@@ -53,6 +54,8 @@
 protected IJavaSearchScope scope;
 protected SearchParticipant participant;
 protected IndexQueryRequestor requestor;
+protected boolean resolveDocumentForJar;
+protected boolean resolveDocumentForSourceFiles;
 protected boolean areIndexesReady;
 protected AtomicLong executionTime;
 boolean parallel;
@@ -61,12 +64,27 @@
 public static final boolean ENABLE_PARALLEL_SEARCH_DEFAULT = true;
 
 public PatternSearchJob(SearchPattern pattern, SearchParticipant participant, IJavaSearchScope scope, IndexQueryRequestor requestor) {
+	this(pattern, participant, scope, true, true, requestor);
+}
+
+/**
+ * Create a search job with the specified search pattern.
+ *
+ * @param resolveDocumentForJar whether to resolve the document name of a result entry
+ *                              if it comes to a JAR library.
+ * @param resolveDocumentForSourceFiles whether to resolve the document name of a result entry
+ *                                      if it comes from a project's source files.
+ */
+public PatternSearchJob(SearchPattern pattern, SearchParticipant participant, IJavaSearchScope scope, final boolean resolveDocumentForJar, final boolean resolveDocumentForSourceFiles, IndexQueryRequestor requestor) {
 	this.executionTime = new AtomicLong(0);
 	this.pattern = pattern;
 	this.participant = participant;
 	this.scope = scope;
 	this.requestor = requestor;
+	this.resolveDocumentForJar = resolveDocumentForJar;
+	this.resolveDocumentForSourceFiles = resolveDocumentForSourceFiles;
 }
+
 @Override
 public boolean belongsTo(String jobFamily) {
 	return true;
@@ -211,7 +229,16 @@
 			searchPattern = clone(searchPattern);
 			searchScope = clone(searchScope);
 		}
-		MatchLocator.findIndexMatches(searchPattern, index, queryRequestor, this.participant, searchScope, progressMonitor);
+
+		boolean isFromJar = index.isIndexForJar();
+		boolean resolveDocumentName = (isFromJar && this.resolveDocumentForJar)
+			|| (!isFromJar && this.resolveDocumentForSourceFiles);
+		if (resolveDocumentName) {
+			// fall back to the default behavior in case some pattern implementation doesn't adapt to the new index search API.
+			MatchLocator.findIndexMatches(searchPattern, index, queryRequestor, this.participant, searchScope, progressMonitor);
+		} else {
+			MatchLocator.findIndexMatches(searchPattern, index, queryRequestor, this.participant, searchScope, false, progressMonitor);
+		}
 		this.executionTime.addAndGet(System.currentTimeMillis() - start);
 		return COMPLETE;
 	} catch (IOException e) {
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 1a53016..540556e 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
@@ -1758,11 +1758,15 @@
 					iterator.remove();
 				}
 			}
-			if(index == null) {
+			if (index == null) {
 				return true;
 			}
+			if (index.monitor == null) {
+				// index got deleted since acquired
+				continue;
+			}
 			File indexFile = index.getIndexFile();
-			if(indexFile == null) {
+			if (indexFile == null) {
 				continue;
 			}
 			if (VERBOSE) {
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IntersectingPattern.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IntersectingPattern.java
index 448eed9..e73eda3 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IntersectingPattern.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IntersectingPattern.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * Copyright (c) 2000, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Microsoft Corporation - adapt to the new index match API
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.search.matching;
 
@@ -30,6 +31,7 @@
 public IntersectingPattern(int patternKind, int matchRule) {
 	super(patternKind, matchRule);
 }
+
 @Override
 public void findIndexMatches(Index index, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, IProgressMonitor progressMonitor) throws IOException {
 	if (progressMonitor != null && progressMonitor.isCanceled()) throw new OperationCanceledException();
@@ -77,6 +79,12 @@
 		if (names[i] != null)
 			acceptMatch((String) names[i], containerPath, separator, null/*no pattern*/, requestor, participant, scope, progressMonitor); // AndPatterns cannot provide the decoded result
 }
+
+@Override
+public void findIndexMatches(Index index, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, boolean resolveDocumentName, IProgressMonitor progressMonitor) throws IOException {
+	findIndexMatches(index, requestor, participant, scope, progressMonitor);
+}
+
 /**
  * Returns whether another query must be done.
  */
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/LocalVariablePattern.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/LocalVariablePattern.java
index 7bd60b0..01b6ff8 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/LocalVariablePattern.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/LocalVariablePattern.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * Copyright (c) 2000, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Microsoft Corporation - adapt to the new index match API
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.search.matching;
 
@@ -70,6 +71,12 @@
 			throw new OperationCanceledException();
 	}
 }
+
+@Override
+public void findIndexMatches(Index index, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, boolean resolveDocumentName, IProgressMonitor progressMonitor) {
+	findIndexMatches(index, requestor, participant, scope, progressMonitor);
+}
+
 @Override
 protected StringBuffer print(StringBuffer output) {
 	if (this.findDeclarations) {
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 0129c95..0e90cc7 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
@@ -14,6 +14,7 @@
  *     Technical University Berlin - extended API and implementation
  *     Stephan Herrmann - Contribution for
  *								Bug 377883 - NPE on open Call Hierarchy
+ *     Microsoft Corporation - Contribution for bug 575562 - improve completion search performance
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.search.matching;
 
@@ -361,6 +362,13 @@
 	pattern.findIndexMatches(index, requestor, participant, scope, monitor);
 }
 
+/**
+ * Query a given index for matching entries. Assumes the sender has opened the index and will close when finished.
+ */
+public static void findIndexMatches(SearchPattern pattern, Index index, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, boolean resolveDocumentName, IProgressMonitor monitor) throws IOException {
+	pattern.findIndexMatches(index, requestor, participant, scope, resolveDocumentName, monitor);
+}
+
 public static IJavaElement getProjectOrJar(IJavaElement element) {
 	while (!(element instanceof IJavaProject) && !(element instanceof JarPackageFragmentRoot)) {
 		element = element.getParent();
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/OrPattern.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/OrPattern.java
index 386435d..ad2bcb2 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/OrPattern.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/OrPattern.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * Copyright (c) 2000, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Microsoft Corporation - adapt to the new index match API
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.search.matching;
 
@@ -70,6 +71,18 @@
 	}
 
 	@Override
+	public void findIndexMatches(Index index, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, boolean resolveDocumentName, IProgressMonitor progressMonitor) throws IOException {
+		// per construction, OR pattern can only be used with a PathCollector (which already gather results using a set)
+		try {
+			index.startQuery();
+			for (int i = 0, length = this.patterns.length; i < length; i++)
+				this.patterns[i].findIndexMatches(index, requestor, participant, scope, resolveDocumentName, progressMonitor);
+		} finally {
+			index.stopQuery();
+		}
+	}
+
+	@Override
 	public SearchPattern getBlankPattern() {
 		return null;
 	}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeParameterPattern.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeParameterPattern.java
index e3d2083..b2c6ff0 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeParameterPattern.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeParameterPattern.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * Copyright (c) 2000, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Microsoft Corporation - adapt to the new index match API
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.search.matching;
 
@@ -118,6 +119,11 @@
 	}
 
 	@Override
+	public void findIndexMatches(Index index, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, boolean resolveDocumentName, IProgressMonitor progressMonitor) {
+		findIndexMatches(index, requestor, participant, scope, progressMonitor);
+	}
+
+	@Override
 	protected StringBuffer print(StringBuffer output) {
 		if (this.findDeclarations) {
 			output.append(this.findReferences
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 d0eadca..490ce51 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
@@ -48,6 +48,8 @@
 
 	private int awaitingClients = 0;
 
+	private final Object idleMonitor = new Object();
+
 	/**
 	 * Invoked exactly once, in background, before starting processing any job
 	 */
@@ -251,6 +253,11 @@
 									throw new OperationCanceledException();
 								IJob currentJob = currentJob();
 								// currentJob can be null when jobs have been added to the queue but job manager is not enabled
+								if (currentJob != null ) {
+									synchronized (this.idleMonitor) {
+										this.idleMonitor.notifyAll(); // wake up idle sleepers
+									}
+								}
 								if (currentJob != null && currentJob != previousJob) {
 									if (VERBOSE)
 										Util.verbose("-> NOT READY - waiting until ready - " + searchJob);//$NON-NLS-1$
@@ -414,7 +421,9 @@
 					if (job == null) {
 						notifyIdle(System.currentTimeMillis() - idlingStart);
 						// just woke up, delay before processing any new jobs, allow some time for the active thread to finish
-						Thread.sleep(500);
+						synchronized (this.idleMonitor) {
+							this.idleMonitor.wait(500); // avoid sleep fixed time
+						}
 						continue;
 					}
 					if (VERBOSE) {
@@ -440,7 +449,9 @@
 							if (VERBOSE) {
 								Util.verbose("WAITING after job - " + job); //$NON-NLS-1$
 							}
-							Thread.sleep(5);
+							synchronized (this.idleMonitor) {
+								this.idleMonitor.wait(5); // avoid sleep fixed time
+							}
 						}
 					}
 				} catch (InterruptedException e) { // background indexing was interrupted