Bug 15589 - code assist adds extra semicolons in import type
declarations

Change-Id: I2a8db9197f590e2633e13f8621026db476b0d2d0
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 a1431bf..dadb6be 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
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2012 IBM Corporation and others.
+ * Copyright (c) 2005, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -2249,7 +2249,7 @@
 		"completion token kind=TOKEN_KIND_NAME\n" +
 		"expectedTypesSignatures=null\n" +
 		"expectedTypesKeys=null\n"+
-		"completion token location=UNKNOWN",
+		"completion token location={IN_IMPORT}",
 		result.context);
 }
 public void test0082() throws JavaModelException {
@@ -2275,7 +2275,7 @@
 		"completion token kind=TOKEN_KIND_NAME\n" +
 		"expectedTypesSignatures=null\n" +
 		"expectedTypesKeys=null\n"+
-		"completion token location=UNKNOWN",
+		"completion token location={IN_IMPORT}",
 		result.context);
 }
 public void test0083() throws JavaModelException {
@@ -2301,7 +2301,7 @@
 		"completion token kind=TOKEN_KIND_NAME\n" +
 		"expectedTypesSignatures=null\n" +
 		"expectedTypesKeys=null\n"+
-		"completion token location=UNKNOWN",
+		"completion token location={IN_IMPORT}",
 		result.context);
 }
 public void test0084() throws JavaModelException {
@@ -2327,7 +2327,7 @@
 		"completion token kind=TOKEN_KIND_NAME\n" +
 		"expectedTypesSignatures=null\n" +
 		"expectedTypesKeys=null\n"+
-		"completion token location=UNKNOWN",
+		"completion token location={IN_IMPORT}",
 		result.context);
 }
 public void test0085() throws JavaModelException {
@@ -2353,7 +2353,7 @@
 		"completion token kind=TOKEN_KIND_NAME\n" +
 		"expectedTypesSignatures=null\n" +
 		"expectedTypesKeys=null\n"+
-		"completion token location=UNKNOWN",
+		"completion token location={IN_IMPORT}",
 		result.context);
 }
 public void test0086() throws JavaModelException {
@@ -2379,7 +2379,7 @@
 		"completion token kind=TOKEN_KIND_NAME\n" +
 		"expectedTypesSignatures=null\n" +
 		"expectedTypesKeys=null\n"+
-		"completion token location=UNKNOWN",
+		"completion token location={IN_IMPORT}",
 		result.context);
 }
 public void test0087() throws JavaModelException {
@@ -2405,7 +2405,7 @@
 		"completion token kind=TOKEN_KIND_NAME\n" +
 		"expectedTypesSignatures=null\n" +
 		"expectedTypesKeys=null\n"+
-		"completion token location=UNKNOWN",
+		"completion token location={IN_IMPORT}",
 		result.context);
 }
 public void test0088() throws JavaModelException {
@@ -2431,7 +2431,7 @@
 		"completion token kind=TOKEN_KIND_NAME\n" +
 		"expectedTypesSignatures=null\n" +
 		"expectedTypesKeys=null\n"+
-		"completion token location=UNKNOWN",
+		"completion token location={IN_IMPORT}",
 		result.context);
 }
 public void test0089() throws JavaModelException {
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 6ac1082..6f64a96 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
@@ -6336,7 +6336,7 @@
 	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
 
 	assertResults(
-			"AClass2[TYPE_REF]{test.p.AClass2;, test.p, Ltest.p.AClass2;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_RESTRICTED) + "}",
+			"AClass2[TYPE_REF]{test.p.AClass2, test.p, Ltest.p.AClass2;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_RESTRICTED) + "}",
 			requestor.getResults());
 }
 
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 c95e87e..3f91659 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
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2018 IBM Corporation and others.
+ * Copyright (c) 2004, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -206,6 +206,11 @@
 					buffer.append("CONSTRUCTOR_START"); //$NON-NLS-1$
 					first = false;
 				}
+				if ((locationType & CompletionContext.TL_IN_IMPORT) != 0) {
+					if (!first) buffer.append(',');
+					buffer.append("IN_IMPORT"); //$NON-NLS-1$
+					first = false;
+				}
 				buffer.append('}');
 			}
 		}
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 faf227c..fa94e57 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
@@ -14615,5 +14615,17 @@
 	assertTrue(!requestor.getResults().equals(""));
 	assertTrue(requestor.getResults().contains("toString("));
 }
+public void testBug15589() throws JavaModelException {
+	CompletionResult result = complete(
+            "/Completion/src3/bug15589/Test.java",
+            "package bug15589;\n" +
+            "import java.util.Coll;\n" +
+            "public class Test {\n" +
+            "}\n",
+            "import java.util.Coll");
 
+    assertResults(
+            "Collection[TYPE_REF]{Collection, java.util, Ljava.util.Collection;, null, null, "+(R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_RESTRICTED)+"}",
+            result.proposals);
+}
 }
diff --git a/org.eclipse.jdt.core/.settings/.api_filters b/org.eclipse.jdt.core/.settings/.api_filters
new file mode 100644
index 0000000..82601f8
--- /dev/null
+++ b/org.eclipse.jdt.core/.settings/.api_filters
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.jdt.core" version="2">
+    <resource path="model/org/eclipse/jdt/core/CompletionContext.java" type="org.eclipse.jdt.core.CompletionContext">
+        <filter comment="Contract of getTokenLocation() announces future additions" id="336658481">
+            <message_arguments>
+                <message_argument value="org.eclipse.jdt.core.CompletionContext"/>
+                <message_argument value="TL_IN_IMPORT"/>
+            </message_arguments>
+        </filter>
+    </resource>
+</component>
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 875818b..de893ef 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
@@ -51,6 +51,7 @@
 import org.eclipse.jdt.core.compiler.CategorizedProblem;
 import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.compiler.InvalidInputException;
 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
 import org.eclipse.jdt.core.search.IJavaSearchConstants;
 import org.eclipse.jdt.core.search.IJavaSearchScope;
@@ -1503,10 +1504,10 @@
 						} else if ((modifiers & ClassFileConstants.AccStatic) == 0) {
 							continue next;
 						} else {
-							completionName = CharOperation.concat(completionName, new char[] { ';' });
+							completionName = appendUnlessNextToken(completionName, new char[] { ';' }, TerminalTokens.TokenNameSEMICOLON);
 						}
 					} else {
-						completionName = CharOperation.concat(completionName, new char[] { ';' });
+						completionName = appendUnlessNextToken(completionName, new char[] {';'}, TerminalTokens.TokenNameSEMICOLON);
 					}
 	
 					int relevance = computeBaseRelevance();
@@ -1665,6 +1666,18 @@
 		}
 	}
 	
+	private char[] appendUnlessNextToken(char[] completionName, char[] suffix, int nextToken) {
+		this.parser.scanner.resetTo(this.endPosition, Integer.MAX_VALUE);
+		try {
+			if (this.parser.scanner.getNextToken() != nextToken) {
+				return CharOperation.concat(completionName, suffix);
+			}
+		} catch (InvalidInputException e) {
+			// ignore
+		}
+		return completionName;
+	}
+
 	public void acceptUnresolvedName(char[] name) {
 		int relevance = computeBaseRelevance();
 		relevance += computeRelevanceForResolution(false);
@@ -1862,6 +1875,8 @@
 			}
 		} else if (astNode instanceof CompletionOnKeyword3 && ((CompletionOnKeyword3) astNode).afterTryOrCatch()) {
 				context.setTokenLocation(CompletionContext.TL_STATEMENT_START);
+		} else if (astNode instanceof CompletionOnImportReference) {
+			context.setTokenLocation(CompletionContext.TL_IN_IMPORT);
 		} else {
 			ReferenceContext referenceContext = scope.referenceContext();
 			if (referenceContext instanceof AbstractMethodDeclaration) {
@@ -2183,7 +2198,7 @@
 							this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
 							if ((this.unitScope = parsedUnit.scope) != null) {
 								contextAccepted = true;
-								buildContext(importReference, null, parsedUnit, null, null);
+								buildContext(importReference, null, parsedUnit, null, this.unitScope);
 
 								long positions = importReference.sourcePositions[importReference.tokens.length - 1];
 								setSourceAndTokenRange((int) (positions >>> 32), (int) positions);
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/CompletionContext.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/CompletionContext.java
index 89f1264..53ee512 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/CompletionContext.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/CompletionContext.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2012 IBM Corporation and others.
+ * Copyright (c) 2005, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -75,6 +75,19 @@
 	public static final int TL_CONSTRUCTOR_START = 4;
 
 	/**
+	 * The completed token is part of an import statement<br>
+	 * e.g.
+	 * <pre>
+	 * import java.util| // completion occurs at |
+	 * </pre>
+	 *
+	 * @see #getTokenLocation()
+	 *
+	 * @since 3.21
+	 */
+	public static final int TL_IN_IMPORT = 8;
+
+	/**
 	 * The completion token is unknown.
 	 * @since 3.2
 	 */