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>
<!--