Merge "Bug 574039 - [17] Merge master to BETA_JAVA17 periodically" into BETA_JAVA17
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionParserTest2.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionParserTest2.java
index 14f7784..7247ee2 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionParserTest2.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionParserTest2.java
@@ -7346,7 +7346,7 @@
 	String expectedCompletionNodeToString = "<CompleteOnName:zzz>";
 	String expectedParentNodeToString = "(1 == <CompleteOnName:zzz>)";
 	String completionIdentifier = "zzz";
-	String expectedReplacedSource = "(zzz)";
+	String expectedReplacedSource = "zzz";
 	String expectedUnitDisplayString =
 			"package p;\n" +
 			"public class X {\n" +
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/AbstractCompilerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/AbstractCompilerTest.java
index 6b0ae18..164dad7 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/AbstractCompilerTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/AbstractCompilerTest.java
@@ -329,7 +329,11 @@
 			isJRE10Plus = isJRE11Plus || CompilerOptions.VERSION_10.equals(specVersion);
 			isJRE9Plus = isJRE10Plus || CompilerOptions.VERSION_9.equals(specVersion);
 			initReflectionVersion();
-			String compliances = System.getProperty("compliance");
+			String key = "compliance.jre." + specVersion;
+			String compliances = System.getProperty(key);
+			if (compliances == null) {
+				compliances = System.getProperty("compliance");
+			}
 			if (compliances != null) {
 				possibleComplianceLevels = 0;
 				for (String compliance : compliances.split(",")) {
diff --git a/org.eclipse.jdt.core.tests.compiler/test.xml b/org.eclipse.jdt.core.tests.compiler/test.xml
index afe7e7c..53a3377 100644
--- a/org.eclipse.jdt.core.tests.compiler/test.xml
+++ b/org.eclipse.jdt.core.tests.compiler/test.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <!--
-    Copyright (c) 2002, 2014 IBM Corporation and others.
+    Copyright (c) 2002, 2021 IBM Corporation and others.
 
     This program and the accompanying materials
     are made available under the terms of the Eclipse Public License 2.0
@@ -56,6 +56,9 @@
       <property name="plugin-name" value="${plugin-name}"/>
       <property name="classname" 
                 value="org.eclipse.jdt.core.tests.compiler.regression.TestAll"/>
+      <property name="vmargs" 
+      	value="-Dcompliance.jre.11=1.8,9,10,11 -Dcompliance.jre.16=1.8,11,15,16 -Dcompliance.jre.17=1.8,11,16,17"
+      />
     </ant>
 
 	<antcall target="evaluation_tests"/>
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 2262a20..d6b8dd6 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
@@ -922,6 +922,7 @@
 		int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
 		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
 		assertResults(
+				"for[KEYWORD]{for, null, null, for, null, 49}\n" +
 				"fooo[LOCAL_VARIABLE_REF]{fooo, null, Ljava.lang.String;, fooo, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}",
 				requestor.getResults());
 	} finally {
@@ -1165,4 +1166,100 @@
 		deleteProject("P");
 	}
 }
+public void testBug575397a() 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",
+			"class Thread {\n" +
+			"	static void sleep(int millis) {}\n" +
+			"}\n" +
+			"public class ContentAssist {\n" +
+			"	protected void test() {\n" +
+			"		if (true) {\n" +
+			"			Thread.\n" +
+			"			someMethod();\n" +
+			"		}\n" +
+			"	}\n" +
+			"	void someMethod() { }\n" +
+			"}\n");
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+		String str = this.workingCopies[0].getSource();
+		String completeAfter = "Thread.";
+		int cursorLocation = str.indexOf(completeAfter) + completeAfter.length();
+		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+		assertResults(
+			"sleep[METHOD_REF]{sleep(), LThread;, (I)V, sleep, (millis), 51}",
+			requestor.getResults());
+	} finally {
+		deleteProject("P");
+	}
+}
+public void testBug575397b() 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",
+			"class Thread {\n" +
+			"	static void sleep(int millis) {}\n" +
+			"	public enum State { NEW, BLOCKED }\n" +
+			"}\n" +
+			"public class ContentAssist {\n" +
+			"	protected void test() {\n" +
+			"		if (true) {\n" +
+			"			Thread.Sta\n" +
+			"			someMethod();\n" +
+			"		}\n" +
+			"	}\n" +
+			"	void someMethod() { }\n" +
+			"}\n");
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+		String str = this.workingCopies[0].getSource();
+		String completeAfter = "Thread.Sta";
+		int cursorLocation = str.indexOf(completeAfter) + completeAfter.length();
+		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+		assertResults(
+			"Thread.State[TYPE_REF]{State, , LThread$State;, null, null, 49}",
+			requestor.getResults());
+	} finally {
+		deleteProject("P");
+	}
+}
+public void testBug575397c() 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",
+			"class Thread {\n" +
+			"	static void sleep(int millis) {}\n" +
+			"	public enum State { NEW, BLOCKED }\n" +
+			"}\n" +
+			"public class ContentAssist {\n" +
+			"	protected void test() {\n" +
+			"		if (true) {\n" +
+			"			Thread.State.\n" +
+			"			someMethod();\n" +
+			"		}\n" +
+			"	}\n" +
+			"	void someMethod() { }\n" +
+			"}\n");
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+		String str = this.workingCopies[0].getSource();
+		String completeAfter = "Thread.State.";
+		int cursorLocation = str.indexOf(completeAfter) + completeAfter.length();
+		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+		assertResults(
+			"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" +
+			"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());
+	} finally {
+		deleteProject("P");
+	}
+}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/MementoTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/MementoTests.java
index 5711170..221adc1 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/MementoTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/MementoTests.java
@@ -77,7 +77,9 @@
 	return getEscapedPath(getExternalJCLPath().toString());
 }
 String getEscapedJrtJarPath() {
-	return getEscapedPath(System.getProperty("java.home")+"/lib/jrt-fs.jar");
+	String pathStr = System.getProperty("java.home")+"/lib/jrt-fs.jar";
+	String path = new Path(pathStr).toPortableString();
+	return getEscapedPath(path);
 }
 protected String getEscapedPath(String path) {
 	StringBuilder buffer = new StringBuilder();
@@ -875,8 +877,9 @@
 		String expectedIdentifier = "=Test/"+getEscapedJrtJarPath()+"`java.base"+attributesMemento; // for specific PFR (see below)
 		IModuleDescription module = project.findModule("java.base", null);
 		String moduleIdentifier = expectedIdentifier+"<'`java.base"; // PFR - PackageFragment - ModularClassFile - Module
-		assertEquals("Module mementos", moduleIdentifier, module.getHandleIdentifier());
-		IJavaElement module2 = JavaCore.create(module.getHandleIdentifier(), null);
+		String moduleHandleIdentifier = module.getHandleIdentifier();
+		assertEquals("Module mementos", moduleIdentifier, moduleHandleIdentifier);
+		IJavaElement module2 = JavaCore.create(moduleHandleIdentifier, null);
 		assertTrue("Module existence", module2.exists());
 		assertEquals("Module equivalence", module, module2);
 
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 bd98db1..0002480 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
@@ -3694,6 +3694,17 @@
 				}
 			}
 		} 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);
 
@@ -3702,7 +3713,7 @@
 				if (this.assistNodeIsException && astNodeParent instanceof TryStatement) {
 					findExceptionFromTryStatement(
 							this.completionToken,
-							(ReferenceBinding)qualifiedBinding,
+							receiverType,
 							scope.enclosingSourceType(),
 							(BlockScope)scope,
 							typesFound);
@@ -3712,7 +3723,7 @@
 
 				findMemberTypes(
 					this.completionToken,
-					(ReferenceBinding) qualifiedBinding,
+					receiverType,
 					scope,
 					scope.enclosingSourceType(),
 					false,
@@ -3933,8 +3944,15 @@
 			if (receiverType != null && receiverType.isValidBinding()) {
 				findVariablesAndMethods(this.completionToken, scope, FakeInvocationSite, scope, false, false);
 			}
+			// ... or a keyword (possibly starting a new statement):
+			if (!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
+				if (this.completionToken != null && this.completionToken.length != 0) {
+					findKeywords(this.completionToken, singleRef.possibleKeywords, false, false);
+				} else {
+					findTrueOrFalseKeywords(singleRef.possibleKeywords);
+				}
+			}
 		}
-
 	}
 
 	private void completionOnProvidesInterfacesSingleTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
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 c69faaf..4ab064b 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,6 +40,7 @@
 	public char[] completionIdentifier;
 
 	public boolean isConstructorType;
+	public int nextToken;
 
 public CompletionOnQualifiedTypeReference(char[][] previousIdentifiers, char[] completionIdentifier, long[] positions) {
 	this(previousIdentifiers, completionIdentifier, positions, K_TYPE);
@@ -49,6 +50,10 @@
 	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/CompletionOnSingleTypeReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnSingleTypeReference.java
index 4dc9c77..27eceba 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnSingleTypeReference.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnSingleTypeReference.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
@@ -40,6 +40,8 @@
 public boolean isCompletionNode;
 public boolean isConstructorType;
 public CompletionOnFieldType fieldTypeCompletionNode;
+public char[][] possibleKeywords;
+public boolean canBeExplicitConstructor;
 
 public CompletionOnSingleTypeReference(char[] source, long pos) {
 	this(source, pos, K_TYPE);
@@ -49,6 +51,11 @@
 	this.isCompletionNode = true;
 	this.kind = kind;
 }
+public CompletionOnSingleTypeReference(char[] assistName, long position, char[][] keywords, boolean canBeSuperCall) {
+	this(assistName, position);
+	this.possibleKeywords = keywords;
+	this.canBeExplicitConstructor = canBeSuperCall;
+}
 @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 8f8643d..310dbc5 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
@@ -18,6 +18,8 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.codeassist.complete;
 
+import java.util.ArrayList;
+
 /*
  * Parser able to build specific completion parse nodes, given a cursorLocation.
  *
@@ -29,6 +31,7 @@
  */
 
 import java.util.HashSet;
+import java.util.List;
 
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.jdt.core.compiler.CharOperation;
@@ -5026,7 +5029,7 @@
 		if (isAfterWithClause()) return new CompletionOnProvidesImplementationsQualifiedTypeReference(previousIdentifiers, assistName, positions);
 		return new CompletionOnProvidesInterfacesQualifiedTypeReference(previousIdentifiers, assistName, positions);
 	}
-	return new CompletionOnQualifiedTypeReference(previousIdentifiers,	assistName,	positions);
+	return null;
 }
 @Override
 public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] assistName, long[] positions){
@@ -5052,10 +5055,14 @@
 					positions,
 					CompletionOnQualifiedTypeReference.K_INTERFACE);
 		default :
-			return checkAndCreateModuleQualifiedAssistTypeReference(
+			TypeReference ref = checkAndCreateModuleQualifiedAssistTypeReference(
 					previousIdentifiers,
 					assistName,
 					positions);
+			if (ref != null)
+				return ref;
+			return new CompletionOnQualifiedTypeReference(previousIdentifiers, assistName, positions,
+					CompletionOnQualifiedTypeReference.K_TYPE, this.currentToken);
 	}
 }
 @Override
@@ -5130,128 +5137,133 @@
 			&& topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == SWITCH) {
 			return new CompletionOnKeyword3(assistName, position, new char[][]{Keywords.CASE, Keywords.DEFAULT}, false);
 		} else {
-			char[][] keywords = new char[Keywords.COUNT][];
-			int count = 0;
-
-			if((this.lastModifiers & ClassFileConstants.AccStatic) == 0) {
-				keywords[count++]= Keywords.SUPER;
-				keywords[count++]= Keywords.THIS;
-			}
-			keywords[count++]= Keywords.NEW;
-			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=269493: Keywords are not proposed in a for
-			// loop without block. Completion while at K_CONTROL_STATEMENT_DELIMITER case needs to handled
-			// similar to the K_BLOCK_DELIMITER with minor differences.
-			if(kind == K_BLOCK_DELIMITER || kind == K_CONTROL_STATEMENT_DELIMITER || kind == K_LAMBDA_EXPRESSION_DELIMITER
-					|| kind == K_SWITCH_EXPRESSION_DELIMITTER) {
-				if(this.canBeExplicitConstructor == YES) {
-					canBeExplicitConstructorCall = true;
-				}
-				if (this.options.complianceLevel >= ClassFileConstants.JDK1_4) {
-					keywords[count++]= Keywords.ASSERT;
-				}
-				keywords[count++]= Keywords.DO;
-				keywords[count++]= Keywords.FOR;
-				keywords[count++]= Keywords.IF;
-				keywords[count++]= Keywords.RETURN;
-				keywords[count++]= Keywords.SWITCH;
-				keywords[count++]= Keywords.SYNCHRONIZED;
-				keywords[count++]= Keywords.THROW;
-				keywords[count++]= Keywords.TRY;
-				keywords[count++]= Keywords.WHILE;
-
-				keywords[count++]= Keywords.FINAL;
-				keywords[count++]= Keywords.CLASS;
-				if (this.options.complianceLevel >= ClassFileConstants.JDK10) {
-					keywords[count++]= Keywords.VAR;
-				}
-				if (this.options.complianceLevel >= ClassFileConstants.JDK16) {
-					keywords[count++]= Keywords.INTERFACE;
-					keywords[count++]= Keywords.ENUM;
-				}
-
-				if(this.previousKind == K_BLOCK_DELIMITER) {
-					switch (this.previousInfo) {
-						case IF :
-							keywords[count++]= Keywords.ELSE;
-							break;
-						case CATCH :
-							keywords[count++]= Keywords.CATCH;
-							keywords[count++]= Keywords.FINALLY;
-							break;
-					}
-				} else if(this.previousKind == K_CONTROL_STATEMENT_DELIMITER && this.previousInfo == IF) {
-					keywords[count++]= Keywords.ELSE;
-				}
-				if(isInsideLoop()) {
-					keywords[count++]= Keywords.CONTINUE;
-				}
-				if(isInsideBreakable()) {
-					keywords[count++]= Keywords.BREAK;
-				}
-				if(isInsideSwitch()) {
-					keywords[count++]= Keywords.YIELD;
-				}
-			} else if (kind == K_BETWEEN_FOR_AND_RIGHT_PAREN) {
-				if (this.options.complianceLevel >= ClassFileConstants.JDK10) {
-					keywords[count++]= Keywords.VAR;
-				}
-			} else if(kind != K_BETWEEN_CASE_AND_COLON && kind != K_BETWEEN_DEFAULT_AND_COLON) {
-				if (kind == K_LOCAL_INITIALIZER_DELIMITER && this.options.complianceLevel >= ClassFileConstants.JDK11) {
-					keywords[count++]= Keywords.VAR;
-				}
-				if (kind == K_SELECTOR_QUALIFIER && this.options.complianceLevel >= ClassFileConstants.JDK12) {
-					keywords[count++] = Keywords.SWITCH;
-				}
-				keywords[count++]= Keywords.TRUE;
-				keywords[count++]= Keywords.FALSE;
-				keywords[count++]= Keywords.NULL;
-				if (kind == K_YIELD_KEYWORD) {
-					keywords[count++]= Keywords.YIELD;
-				}
-				if(kind == K_SWITCH_LABEL) {
-					if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) != DEFAULT) {
-						keywords[count++]= Keywords.DEFAULT;
-					}
-					keywords[count++]= Keywords.BREAK;
-					keywords[count++]= Keywords.CASE;
-					keywords[count++]= Keywords.YIELD;
-					if (this.options.complianceLevel >= ClassFileConstants.JDK1_4) {
-						keywords[count++]= Keywords.ASSERT;
-					}
-					keywords[count++]= Keywords.DO;
-					keywords[count++]= Keywords.FOR;
-					keywords[count++]= Keywords.IF;
-					keywords[count++]= Keywords.RETURN;
-					keywords[count++]= Keywords.SWITCH;
-					keywords[count++]= Keywords.SYNCHRONIZED;
-					keywords[count++]= Keywords.THROW;
-					keywords[count++]= Keywords.TRY;
-					keywords[count++]= Keywords.WHILE;
-
-					keywords[count++]= Keywords.FINAL;
-					keywords[count++]= Keywords.CLASS;
-
-					if (this.options.complianceLevel >= ClassFileConstants.JDK10) {
-						keywords[count++]= Keywords.VAR;
-					}
-					if(isInsideLoop()) {
-						keywords[count++]= Keywords.CONTINUE;
-					}
-				}
-			}
-			System.arraycopy(keywords, 0 , keywords = new char[count][], 0, count);
-
+			List<char[]> keywordsList = new ArrayList<>(Keywords.COUNT);
+			canBeExplicitConstructorCall = computeKeywords(kind, keywordsList);
+			char[][] keywords = keywordsList.toArray(char[][]::new);
 			return new CompletionOnSingleNameReference(assistName, position, keywords, canBeExplicitConstructorCall, isInsideAttributeValue());
 		}
 	}
 }
+boolean computeKeywords(int kind, List<char[]> keywords) {
+	boolean canBeExplicitConstructorCall = false;
+
+	if((this.lastModifiers & ClassFileConstants.AccStatic) == 0) {
+		keywords.add(Keywords.SUPER);
+		keywords.add(Keywords.THIS);
+	}
+	keywords.add(Keywords.NEW);
+	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=269493: Keywords are not proposed in a for
+	// loop without block. Completion while at K_CONTROL_STATEMENT_DELIMITER case needs to handled
+	// similar to the K_BLOCK_DELIMITER with minor differences.
+	if(kind == K_BLOCK_DELIMITER || kind == K_CONTROL_STATEMENT_DELIMITER || kind == K_LAMBDA_EXPRESSION_DELIMITER
+			|| kind == K_SWITCH_EXPRESSION_DELIMITTER) {
+		if(this.canBeExplicitConstructor == YES) {
+			canBeExplicitConstructorCall = true;
+		}
+		if (this.options.complianceLevel >= ClassFileConstants.JDK1_4) {
+			keywords.add(Keywords.ASSERT);
+		}
+		keywords.add(Keywords.DO);
+		keywords.add(Keywords.FOR);
+		keywords.add(Keywords.IF);
+		keywords.add(Keywords.RETURN);
+		keywords.add(Keywords.SWITCH);
+		keywords.add(Keywords.SYNCHRONIZED);
+		keywords.add(Keywords.THROW);
+		keywords.add(Keywords.TRY);
+		keywords.add(Keywords.WHILE);
+
+		keywords.add(Keywords.FINAL);
+		keywords.add(Keywords.CLASS);
+		if (this.options.complianceLevel >= ClassFileConstants.JDK10) {
+			keywords.add(Keywords.VAR);
+		}
+		if (this.options.complianceLevel >= ClassFileConstants.JDK16) {
+			keywords.add(Keywords.INTERFACE);
+			keywords.add(Keywords.ENUM);
+		}
+
+		if(this.previousKind == K_BLOCK_DELIMITER) {
+			switch (this.previousInfo) {
+				case IF :
+					keywords.add(Keywords.ELSE);
+					break;
+				case CATCH :
+					keywords.add(Keywords.CATCH);
+					keywords.add(Keywords.FINALLY);
+					break;
+			}
+		} else if(this.previousKind == K_CONTROL_STATEMENT_DELIMITER && this.previousInfo == IF) {
+			keywords.add(Keywords.ELSE);
+		}
+		if(isInsideLoop()) {
+			keywords.add(Keywords.CONTINUE);
+		}
+		if(isInsideBreakable()) {
+			keywords.add(Keywords.BREAK);
+		}
+		if(isInsideSwitch()) {
+			keywords.add(Keywords.YIELD);
+		}
+	} else if (kind == K_BETWEEN_FOR_AND_RIGHT_PAREN) {
+		if (this.options.complianceLevel >= ClassFileConstants.JDK10) {
+			keywords.add(Keywords.VAR);
+		}
+	} else if(kind != K_BETWEEN_CASE_AND_COLON && kind != K_BETWEEN_DEFAULT_AND_COLON) {
+		if (kind == K_LOCAL_INITIALIZER_DELIMITER && this.options.complianceLevel >= ClassFileConstants.JDK11) {
+			keywords.add(Keywords.VAR);
+		}
+		if (kind == K_SELECTOR_QUALIFIER && this.options.complianceLevel >= ClassFileConstants.JDK12) {
+			keywords.add(Keywords.SWITCH);
+		}
+		keywords.add(Keywords.TRUE);
+		keywords.add(Keywords.FALSE);
+		keywords.add(Keywords.NULL);
+		if (kind == K_YIELD_KEYWORD) {
+			keywords.add(Keywords.YIELD);
+		}
+		if(kind == K_SWITCH_LABEL) {
+			if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) != DEFAULT) {
+				keywords.add(Keywords.DEFAULT);
+			}
+			keywords.add(Keywords.BREAK);
+			keywords.add(Keywords.CASE);
+			keywords.add(Keywords.YIELD);
+			if (this.options.complianceLevel >= ClassFileConstants.JDK1_4) {
+				keywords.add(Keywords.ASSERT);
+			}
+			keywords.add(Keywords.DO);
+			keywords.add(Keywords.FOR);
+			keywords.add(Keywords.IF);
+			keywords.add(Keywords.RETURN);
+			keywords.add(Keywords.SWITCH);
+			keywords.add(Keywords.SYNCHRONIZED);
+			keywords.add(Keywords.THROW);
+			keywords.add(Keywords.TRY);
+			keywords.add(Keywords.WHILE);
+
+			keywords.add(Keywords.FINAL);
+			keywords.add(Keywords.CLASS);
+
+			if (this.options.complianceLevel >= ClassFileConstants.JDK10) {
+				keywords.add(Keywords.VAR);
+			}
+			if(isInsideLoop()) {
+				keywords.add(Keywords.CONTINUE);
+			}
+		}
+	}
+	return canBeExplicitConstructorCall;
+}
 private TypeReference checkAndCreateModuleSingleAssistTypeReference(char[] assistName, long position) {
 	if (isInUsesStatement()) return new CompletionOnUsesSingleTypeReference(assistName, position);
 	if (isInProvidesStatement()) {
 		if (isAfterWithClause()) return new CompletionOnProvidesImplementationsSingleTypeReference(assistName, position);
 		return new CompletionOnProvidesInterfacesSingleTypeReference(assistName, position);
 	}
-	return new CompletionOnSingleTypeReference(assistName,position);
+	List<char[]> keywords = new ArrayList<>(Keywords.COUNT);
+	boolean canBeCtorCall = computeKeywords(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER), keywords);
+	return new CompletionOnSingleTypeReference(assistName,position, keywords.toArray(char[][]::new), canBeCtorCall);
 }
 @Override
 public TypeReference createSingleAssistTypeReference(char[] assistName, long position) {
@@ -5460,6 +5472,14 @@
 	return ref;
 }
 @Override
+protected void updateSourcePosition(Expression exp) {
+	// handles total positions of parenthesized expressions, but don't extend position of the assist node:
+	if (exp == this.assistNode)
+		this.intPtr -= 2;
+	else
+		super.updateSourcePosition(exp);
+}
+@Override
 protected void consumePostfixExpression() {
 	// PostfixExpression ::= Name
 	if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) != K_YIELD_KEYWORD) {
@@ -6123,7 +6143,6 @@
 
 	recoveryExitFromVariable();
 }
-
 @Override
 protected CompilationUnitDeclaration endParse(int act) {
 	CompilationUnitDeclaration cud = super.endParse(act);
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
index c84f373..7e27cb9 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
@@ -5229,7 +5229,7 @@
 		return new ClasspathEntry(
 			IPackageFragmentRoot.K_BINARY,
 			IClasspathEntry.CPE_LIBRARY,
-			hasDotDot ? path : JavaProject.canonicalizedPath(path),
+			hasDotDot ? path : JavaProject.createPackageFragementKey(path),
 			ClasspathEntry.INCLUDE_ALL, // inclusion patterns
 			ClasspathEntry.EXCLUDE_NONE, // exclusion patterns
 			sourceAttachmentPath,
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java
index dead751..c10d6ce 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java
@@ -139,7 +139,7 @@
 			if (target instanceof IResource) {
 				tRoot = this.project.getPackageFragmentRoot((IResource)target, this.rootPath, this.extraAttributes);
 			} else {
-				IPath canonicalizedPath = JavaProject.canonicalizedPath(new Path(this.rootPath.toOSString()));
+				IPath canonicalizedPath = JavaProject.createPackageFragementKey(new Path(this.rootPath.toOSString()));
 				tRoot = this.project.getPackageFragmentRoot0(canonicalizedPath, this.extraAttributes);
 			}
 			return tRoot;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java
index cf98f9a..376c6ca 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java
@@ -181,7 +181,7 @@
 	private static final String ASSUMED_EXTERNAL_FILES_CACHE = "assumedExternalFilesCache";  //$NON-NLS-1$
 
 	public static enum ArchiveValidity {
-		BAD_FORMAT, UNABLE_TO_READ, FILE_NOT_FOUND, VALID;
+		INVALID, VALID;
 
 		public boolean isValid() {
 			return this == VALID;
@@ -1329,7 +1329,7 @@
 		}
 
 		public synchronized ClasspathChange resetResolvedClasspath() {
-			// clear non-chaining jars cache and invalid jars cache
+			// clear non-chaining jars cache and external jars cache
 			JavaModelManager.getJavaModelManager().resetClasspathListCache();
 
 			// null out resolved information
@@ -1621,11 +1621,10 @@
 	}
 
 	/*
-	 * A map of IPaths for jars that are known to be invalid (such as not being in a valid/known format), to an eviction timestamp.
-	 * Synchronize on invalidArchivesMutex before accessing.
+	 * A map of IPaths for jars with known validity (such as being in a valid/known format or not), to an eviction timestamp.
+	 * Synchronize on invalidArchives before accessing.
 	 */
 	private final Map<IPath, InvalidArchiveInfo> invalidArchives = new HashMap<>();
-	private final Object invalidArchivesMutex = new Object();
 
 	/*
 	 * A set of IPaths for files that are known to be external to the workspace.
@@ -1797,9 +1796,9 @@
 
 	public void addInvalidArchive(IPath path, ArchiveValidity reason) {
 		if (DEBUG_INVALID_ARCHIVES) {
-			System.out.println("Invalid JAR cache: adding " + path + ", reason: " + reason);  //$NON-NLS-1$//$NON-NLS-2$
+			System.out.println("JAR cache: adding " + reason + " " + path);  //$NON-NLS-1$//$NON-NLS-2$
 		}
-		synchronized (this.invalidArchivesMutex) {
+		synchronized (this.invalidArchives) {
 			this.invalidArchives.put(path, new InvalidArchiveInfo(System.currentTimeMillis() + INVALID_ARCHIVE_TTL_MILLISECONDS, reason));
 		}
 	}
@@ -2841,7 +2840,9 @@
 		if (isJrt(path)) {
 			return;
 		}
-		throwExceptionIfArchiveInvalid(path);
+		if (isArchiveStateKnownToBeValid(path)) {
+			return; // known to be valid
+		}
 		ZipFile file = getZipFile(path);
 		closeZipFile(file);
 	}
@@ -2875,7 +2876,7 @@
 
 	public ZipFile getZipFile(IPath path, boolean checkInvalidArchiveCache) throws CoreException {
 		if (checkInvalidArchiveCache) {
-			throwExceptionIfArchiveInvalid(path);
+			isArchiveStateKnownToBeValid(path);
 		}
 		ZipCache zipCache;
 		ZipFile zipFile;
@@ -2896,17 +2897,11 @@
 			if (zipCache != null) {
 				zipCache.setCache(path, zipFile);
 			}
+			addInvalidArchive(path, ArchiveValidity.VALID); // remember its valid
 			return zipFile;
 		} catch (IOException e) {
-			ArchiveValidity reason;
-
-			if (e instanceof ZipException) {
-				reason = ArchiveValidity.BAD_FORMAT;
-			} else if (e instanceof FileNotFoundException) {
-				reason = ArchiveValidity.FILE_NOT_FOUND;
-			} else {
-				reason = ArchiveValidity.UNABLE_TO_READ;
-			}
+			// file may exist but for some reason is inaccessible
+			ArchiveValidity reason=ArchiveValidity.INVALID;
 			addInvalidArchive(path, reason);
 			throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, e));
 		}
@@ -2932,18 +2927,12 @@
 		return localFile;
 	}
 
-	private void throwExceptionIfArchiveInvalid(IPath path) throws CoreException {
+	private boolean isArchiveStateKnownToBeValid(IPath path) throws CoreException {
 		ArchiveValidity validity = getArchiveValidity(path);
-		IOException reason;
-		switch (validity) {
-			case BAD_FORMAT: reason = new ZipException("Bad format in archive: " + path); break; //$NON-NLS-1$
-			case FILE_NOT_FOUND: reason = new FileNotFoundException("Archive not found for path: " + path); break; //$NON-NLS-1$
-			case UNABLE_TO_READ: reason = new IOException("Unable to read archive: " + path); break; //$NON-NLS-1$
-			default: reason = null;
+		if (validity == null || validity == ArchiveValidity.INVALID) {
+			return false; // chance the file has become accessible/readable now.
 		}
-		if (reason != null) {
-			throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, reason));
-		}
+		return true;
 	}
 
 	/*
@@ -3399,18 +3388,23 @@
 
 	public ArchiveValidity getArchiveValidity(IPath path) {
 		InvalidArchiveInfo invalidArchiveInfo;
-		synchronized (this.invalidArchivesMutex) {
+		synchronized (this.invalidArchives) {
 			invalidArchiveInfo = this.invalidArchives.get(path);
 		}
-		if (invalidArchiveInfo == null)
-			return ArchiveValidity.VALID;
+		if (invalidArchiveInfo == null) {
+			if (DEBUG_INVALID_ARCHIVES) {
+				System.out.println("JAR cache: UNKNOWN validity for " + path);  //$NON-NLS-1$
+			}
+			return null;
+		}
 		long now = System.currentTimeMillis();
 
 		// If the TTL for this cache entry has expired, directly check whether the archive is still invalid.
 		// If it transitioned to being valid, remove it from the cache and force an update to project caches.
 		if (now > invalidArchiveInfo.evictionTimestamp) {
 			try {
-				getZipFile(path, false);
+				ZipFile zipFile = getZipFile(path, false);
+				closeZipFile(zipFile);
 				removeFromInvalidArchiveCache(path);
 			} catch (CoreException e) {
 				// Archive is still invalid, fall through to reporting it is invalid.
@@ -3418,14 +3412,24 @@
 			// Retry the test from the start, now that we have an up-to-date result
 			return getArchiveValidity(path);
 		}
+		if (DEBUG_INVALID_ARCHIVES) {
+			System.out.println("JAR cache: " + invalidArchiveInfo.reason + " " + path);  //$NON-NLS-1$ //$NON-NLS-2$
+		}
 		return invalidArchiveInfo.reason;
 	}
 
 	public void removeFromInvalidArchiveCache(IPath path) {
-		synchronized(this.invalidArchivesMutex) {
+		synchronized(this.invalidArchives) {
+			InvalidArchiveInfo entry = this.invalidArchives.get(path);
+			if (entry != null && entry.reason == ArchiveValidity.VALID) {
+				if (DEBUG_INVALID_ARCHIVES) {
+					System.out.println("JAR cache: keep VALID " + path);  //$NON-NLS-1$
+				}
+				return; // do not remove the VALID information
+			}
 			if (this.invalidArchives.remove(path) != null) {
 				if (DEBUG_INVALID_ARCHIVES) {
-					System.out.println("Invalid JAR cache: removed " + path);  //$NON-NLS-1$
+					System.out.println("JAR cache: removed INVALID " + path);  //$NON-NLS-1$
 				}
 				try {
 					// Bug 455042: Force an update of the JavaProjectElementInfo project caches.
@@ -4292,16 +4296,6 @@
 	public void resetClasspathListCache() {
 		if (this.nonChainingJars != null)
 			this.nonChainingJars.clear();
-		if (DEBUG_INVALID_ARCHIVES) {
-			synchronized(this.invalidArchivesMutex) {
-				if (!this.invalidArchives.isEmpty()) {
-					System.out.println("Invalid JAR cache: clearing cache"); //$NON-NLS-1$
-				}
-			}
-		}
-		synchronized(this.invalidArchivesMutex) {
-			this.invalidArchives.clear();
-		}
 		if (this.externalFiles != null)
 			this.externalFiles.clear();
 		if (this.assumedExternalFiles != null)
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 2edef36..69b4760 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
@@ -148,8 +148,14 @@
 
 	/**
 	 * Whether the underlying file system is case sensitive.
+	 * @deprecated case sensitivity is not a constant but can be configured at runtime for individual folders (see bug 571614#c8)
 	 */
-	protected static final boolean IS_CASE_SENSITIVE = !new File("Temp").equals(new File("temp")); //$NON-NLS-1$ //$NON-NLS-2$
+	@Deprecated
+	private static final boolean IS_CASE_SENSITIVE = !new File("Temp").equals(new File("temp")); //$NON-NLS-1$ //$NON-NLS-2$
+	/**
+	 * ignore case insensitivity by default (see bug 571614#c8)
+	 */
+	static final boolean RESOLVE_ACTUAL_PACKAGEFRAGMENT_NAME = Boolean.getBoolean("org.eclipse.jdt.resolve_actual_packagefragment_name"); //$NON-NLS-1$
 
 	/**
 	 * An empty array of strings indicating that a project doesn't have any prerequesite projects.
@@ -308,20 +314,47 @@
 	}
 
 	/**
+	 * Does nothing by default. With system flag org.eclipse.jdt.resolve_actual_packagefragment_name=true it tries to find the actual filename
+	 */
+	public static IPath createPackageFragementKey(IPath externalPath) {
+		if (RESOLVE_ACTUAL_PACKAGEFRAGMENT_NAME) { //disabled by default
+			return capitailzePath(externalPath);
+		} else {
+			// trust the UI to already have resolved the actual capitalization:
+			return externalPath;  // do nothing
+		}
+	}
+
+	/**
 	 * Returns a canonicalized path from the given external path.
 	 * Note that the return path contains the same number of segments
 	 * and it contains a device only if the given path contained one.
 	 * @param externalPath IPath
-	 * @see java.io.File for the definition of a canonicalized path
 	 * @return IPath
+	 * @deprecated This method may not do what you expect from its name (see bug 571614):
+	 *             <p> On Linux/Mac (CASE_SENSITIVE by default) it does nothing - even when pointing to a case insensitive filesystem.
+	 *             <p> On Windows (CASE_INSENSITIVE by default) it will find the actual capitalization of all path segments.
+	 *             On Windows it will also resolve 8.3 filenames to its long names.
+	 *             On Windows this results in slow system calls for every segment of the path since JDK 12
+	 *             (see https://bugs.openjdk.java.net/browse/JDK-8207005) - even when the filesystem is configured to
+	 *             be case sensitive and 8.3 name resolving is disabled.
+	 *             <p> ALTERNATIVES:
+	 *             <p> For package fragments use {@link #createPackageFragementKey}
+	 *             <p> For comparing files use {@link java.nio.file.Files#isSameFile}
+	 *         	   <p> For getting the actual capitalization use {@link java.nio.file.Path#toRealPath}
 	 */
+	@Deprecated
 	public static IPath canonicalizedPath(IPath externalPath) {
+		return capitailzePath(externalPath);
+	}
+
+    private static IPath capitailzePath(IPath externalPath) {
 
 		if (externalPath == null)
 			return null;
 
-		if (IS_CASE_SENSITIVE) {
-			return externalPath;
+		if (IS_CASE_SENSITIVE) { // if Linux/Mac
+			return externalPath; // do nothing
 		}
 
 		// if not external path, return original path
@@ -1577,7 +1610,7 @@
 	public IPackageFragment findPackageFragment(IPath path)
 		throws JavaModelException {
 
-		return findPackageFragment0(JavaProject.canonicalizedPath(path));
+		return findPackageFragment0(createPackageFragementKey(path));
 	}
 	/*
 	 * non path canonicalizing version
@@ -1596,7 +1629,7 @@
 	public IPackageFragmentRoot findPackageFragmentRoot(IPath path)
 		throws JavaModelException {
 
-		return findPackageFragmentRoot0(JavaProject.canonicalizedPath(path));
+		return findPackageFragmentRoot0(createPackageFragementKey(path));
 	}
 	/*
 	 * no path canonicalization
@@ -2285,7 +2318,7 @@
 	 */
 	@Override
 	public IPackageFragmentRoot getPackageFragmentRoot(String externalLibraryPath) {
-		return getPackageFragmentRoot0(JavaProject.canonicalizedPath(new Path(externalLibraryPath)), null);
+		return getPackageFragmentRoot0(createPackageFragementKey(new Path(externalLibraryPath)), null);
 	}
 
 	/*
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProjectElementInfo.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProjectElementInfo.java
index 619011c..15ddf3b 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProjectElementInfo.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProjectElementInfo.java
@@ -212,7 +212,7 @@
 		if (cache != null) {
 			for (IPackageFragmentRoot root : cache.allPkgFragmentRootsCache) {
 				IJavaProject rootProject = root.getJavaProject();
-				if (rootProject != this && !rootProject.exists()) {
+				if (rootProject != project && !rootProject.exists()) {
 					cache = null; // force rebuilding
 					break;
 				}
diff --git a/pom.xml b/pom.xml
index 3cc538c..1f66fe2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
   <packaging>pom</packaging>
 
   <properties>
-    <tycho.scmUrl>scm:git:git://git.eclipse.org/gitroot/jdt/eclipse.jdt.core.git</tycho.scmUrl>
+    <tycho.scmUrl>scm:git:https://git.eclipse.org/r/jdt/eclipse.jdt.core.git</tycho.scmUrl>
   </properties>
 
   <!--