Fix for Bug 522613: [9][content assist] No proposal after
provides...with

Change-Id: I84fbf81c7232bb9068f54eb073fdc201207e31da
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests9.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests9.java
index 5f908f9..fa5c7df 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests9.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests9.java
@@ -791,4 +791,34 @@
 		deleteProject(project1);
 	}
 }
+public void test522613_001() throws Exception {
+	IJavaProject project1 = createJavaProject("Completion9_1", new String[] {"src"}, new String[] {"JCL18_LIB"}, "bin", "9");
+	try {
+		project1.open(null);
+		createTypePlus("/Completion9_1/src/", "pack11", "Driver", "", false /* isClass */, true /* createFolder */);
+		createTypePlus("/Completion9_1/src/", "pack11", "CCC", "implements pack11.Driver", true /* isClass */, false /* createFolder */);
+		String filePath1 = "/Completion9_1/src/module-info.java";
+		String completeBehind = "with C";
+		String fileContent1 =  "module first {\n"
+				+ "provides pack11.Driver " + completeBehind
+				+ "}\n";
+		createFile(filePath1, fileContent1);
+		addClasspathEntry(project1, JavaCore.newContainerEntry(new Path("org.eclipse.jdt.MODULE_PATH")));
+
+		project1.close(); // sync
+		project1.open(null);
+
+		int cursorLocation = fileContent1.lastIndexOf(completeBehind) + completeBehind.length();
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2();
+
+		ICompilationUnit unit = getCompilationUnit(filePath1);
+		unit.codeComplete(cursorLocation, requestor);
+
+		String expected = "CCC[TYPE_REF]{pack11.CCC, pack11, Lpack11.CCC;, null, 49}"
+			;
+		assertResults(expected,	requestor.getResults());
+	} finally {
+		deleteProject(project1);
+	}
+}
 }
\ No newline at end of file
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 af5186d..1875040 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
@@ -1392,9 +1392,10 @@
 		if (this.options.checkVisibility) {
 			if((modifiers & ClassFileConstants.AccPublic) == 0) {
 				if((modifiers & ClassFileConstants.AccPrivate) != 0) return;
-
-				char[] currentPackage = CharOperation.concatWith(this.unitScope.fPackage.compoundName, '.');
-				if(!CharOperation.equals(packageName, currentPackage)) return;
+				if (this.moduleDeclaration == null) {
+					char[] currentPackage = CharOperation.concatWith(this.unitScope.fPackage.compoundName, '.');
+					if(!CharOperation.equals(packageName, currentPackage)) return;
+				}
 			}
 		}
 
@@ -2074,57 +2075,66 @@
 							}
 						}
 					}
-					UsesStatement[] uses = this.moduleDeclaration.uses;
-					if (uses != null) {
-						for (int i = 0, l = uses.length; i < l; ++i) {
-							TypeReference usesReference = uses[i].serviceInterface;
-							if (usesReference instanceof CompletionOnUsesSingleTypeReference ||
-									usesReference instanceof CompletionOnUsesQualifiedTypeReference) {
-								this.lookupEnvironment.buildTypeBindings(parsedUnit, null);
-								if ((this.unitScope = parsedUnit.scope) != null) {
-									contextAccepted = true;
-									buildContext(usesReference, null, parsedUnit, null, null);
-									findTypeReferences(usesReference, true);
-									debugPrintf();
+					try {
+						UsesStatement[] uses = this.moduleDeclaration.uses;
+						if (uses != null) {
+							for (int i = 0, l = uses.length; i < l; ++i) {
+								UsesStatement usesStatement = uses[i];
+								this.parser.enclosingNode = usesStatement;
+								TypeReference usesReference = usesStatement.serviceInterface;
+								if (usesReference instanceof CompletionOnUsesSingleTypeReference ||
+										usesReference instanceof CompletionOnUsesQualifiedTypeReference) {
+									contextAccepted = checkForCNF(usesReference, parsedUnit, true);
 									return;
 								}
 							}
 						}
-					}
-					ProvidesStatement[] providesStmts = this.moduleDeclaration.services;
-					for (int i = 0, l = providesStmts != null ? providesStmts.length : 0; i < l; ++i) {
-						ProvidesStatement providesStmt = providesStmts[i];
-						TypeReference pInterface = providesStmt.serviceInterface;
-						if (pInterface instanceof CompletionOnProvidesInterfacesSingleTypeReference ||
-								pInterface instanceof CompletionOnProvidesInterfacesQualifiedTypeReference) {
-							this.lookupEnvironment.buildTypeBindings(parsedUnit, null);
-							if ((this.unitScope = parsedUnit.scope) != null) {
-								contextAccepted = true;
-								buildContext(pInterface, null, parsedUnit, null, null);
-								findTypeReferences(pInterface, true);
-								debugPrintf();
+						ProvidesStatement[] providesStmts = this.moduleDeclaration.services;
+						for (int i = 0, l = providesStmts != null ? providesStmts.length : 0; i < l; ++i) {
+							ProvidesStatement providesStmt = providesStmts[i];
+							this.parser.enclosingNode = providesStmt;
+							TypeReference pInterface = providesStmt.serviceInterface;
+							if (pInterface instanceof CompletionOnProvidesInterfacesSingleTypeReference ||
+									pInterface instanceof CompletionOnProvidesInterfacesQualifiedTypeReference) {
+								contextAccepted = checkForCNF(pInterface, parsedUnit, true);
 								return;
 							}
-						}
-						TypeReference[] implementations = providesStmt.implementations;
-						for (int j = 0, k = implementations.length; j < k; ++j) {
-							TypeReference implementation = implementations[i];
-							
-							if (implementation instanceof CompletionOnProvidesImplementationsSingleTypeReference ||
-									implementation instanceof CompletionOnProvidesImplementationsQualifiedTypeReference) {
-								this.lookupEnvironment.buildTypeBindings(parsedUnit, null);
-								if ((this.unitScope = parsedUnit.scope) != null) {
-									contextAccepted = true;
-									buildContext(implementation, null, parsedUnit, null, null);
-									findImplementations(providesStmt, i/* stmtIndex */, j/* implIndex */);
-									debugPrintf();
+							TypeReference[] implementations = providesStmt.implementations;
+							for (int j = 0, k = implementations.length; j < k; ++j) {
+								TypeReference implementation = implementations[j];
+								if (implementation instanceof CompletionOnProvidesImplementationsSingleTypeReference ||
+										implementation instanceof CompletionOnProvidesImplementationsQualifiedTypeReference) {
+									contextAccepted = checkForCNF(implementation, parsedUnit, false);
 									return;
+								} else if (implementation instanceof CompletionOnKeyword) {
+									contextAccepted = true;
+									processModuleKeywordCompletion(parsedUnit, implementation, (CompletionOnKeyword) implementation);
 								}
-							} else if (implementation instanceof CompletionOnKeyword) {
-								contextAccepted = true;
-								processModuleKeywordCompletion(parsedUnit, implementation, (CompletionOnKeyword) implementation);
 							}
 						}
+					} catch (CompletionNodeFound e) {
+						//					completionNodeFound = true;
+						if (e.astNode != null) {
+							// if null then we found a problem in the completion node
+							if(DEBUG) {
+								System.out.print("COMPLETION - Completion node : "); //$NON-NLS-1$
+								System.out.println(e.astNode.toString());
+								if(this.parser.assistNodeParent != null) {
+									System.out.print("COMPLETION - Parent Node : ");  //$NON-NLS-1$
+									System.out.println(this.parser.assistNodeParent);
+								}
+							}
+							this.lookupEnvironment.unitBeingCompleted = parsedUnit; // better resilient to further error reporting
+							contextAccepted =
+									complete(
+											e.astNode,
+											this.parser.assistNodeParent,
+											this.parser.enclosingNode,
+											parsedUnit,
+											e.qualifiedBinding,
+											e.scope,
+											e.insideTypeAnnotation);
+						}
 					}
 				}
 				// scan the package & import statements first
@@ -2303,6 +2313,26 @@
 		}
 	}
 
+	private boolean checkForCNF(TypeReference ref, CompilationUnitDeclaration parsedUnit, boolean showAll) {
+		this.lookupEnvironment.buildTypeBindings(parsedUnit, null);
+		this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
+		parsedUnit.resolve();
+		if ((this.unitScope = parsedUnit.scope) != null) {
+			if (showAll) {
+				char[][] tokens = ref.getTypeName();
+				char[] typeName = CharOperation.concatWithAll(tokens, '.');
+				if (typeName.length == 0) {
+					buildContext(ref, null, parsedUnit, null, null);
+					this.completionToken = new char[] {'*'};
+					findTypesAndPackages(this.completionToken, this.unitScope, true, true, new ObjectVector());
+					return true;
+				}
+			}
+			parsedUnit.scope.faultInTypes();
+		}
+		return false; // should not come here - will throw exception
+	}
+
 	private boolean completeOnPackageVisibilityStatements(boolean contextAccepted,
 			CompilationUnitDeclaration parsedUnit, PackageVisibilityStatement[] pvsStmts) {
 		for (int i = 0, l = pvsStmts.length; i < l; ++i) {
@@ -3617,8 +3647,7 @@
 	}
 
 	private void completionOnProvidesImplementationsQualifiedTypeReference(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);
+		findImplementations((ProvidesStatement) this.parser.enclosingNode, (TypeReference) astNode);
 	}
 
 	private void completionOnSingleNameReference(ASTNode astNode, ASTNode astNodeParent, Scope scope,
@@ -3755,12 +3784,11 @@
 	}
 
 	private void completionOnProvidesInterfacesSingleTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
-		// TODO : filter the results.
 		completionOnSingleTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
 	}
 	private void completionOnProvidesImplementationsSingleTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
-		// TODO : filter the results.
-		completionOnSingleTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
+		findImplementations((ProvidesStatement) this.parser.enclosingNode, (TypeReference) astNode);
+		// TODO : filter the results - remove packs without a type in impl.
 	}
 
 	private char[][] computeAlreadyDefinedName(
@@ -11850,30 +11878,7 @@
 		return null;
 	}
 
-	private void findTypeReferences(TypeReference reference, boolean findMembers) {
-		char[][] tokens = reference.getTypeName();
-
-		char[] typeName = CharOperation.concatWithAll(tokens, '.');
-
-		if (typeName.length == 0) {
-			this.completionToken = new char[] {'*'};
-		} else if (reference instanceof CompletionOnUsesQualifiedTypeReference ||
-				reference instanceof CompletionOnProvidesInterfacesQualifiedTypeReference) {
-			CompletionOnQualifiedTypeReference qReference = (CompletionOnQualifiedTypeReference) reference;
-			if (qReference.completionIdentifier != null) {
-				this.completionToken = CharOperation.concatAll(typeName, qReference.completionIdentifier, '.');
-			}
-		} else {
-			 char[] lastToken = tokens[tokens.length - 1];
-			 this.completionToken = lastToken != null && lastToken.length == 0 ?
-					 CharOperation.concat(typeName, new char[]{'.'}) :lastToken;
-		}
-		setSourceRange(reference.sourceStart, reference.sourceEnd);
-		findTypesAndPackages(this.completionToken, this.unitScope, true, true, new ObjectVector());
-	}
-
-	private void findImplementations(ProvidesStatement providesStmt, int stmtIndex, int implIndex) {
-		TypeReference reference = providesStmt.implementations[implIndex];
+	private void findImplementations(ProvidesStatement providesStmt, TypeReference reference ) {
 		char[][] tokens = reference.getTypeName();
 		char[] typeName = CharOperation.concatWithAll(tokens, '.');
 
@@ -11890,15 +11895,19 @@
 					 CharOperation.concat(typeName, new char[]{'.'}) :lastToken;
 		}
 		setSourceRange(reference.sourceStart, reference.sourceEnd);
-		findImplementations(this.completionToken, this.unitScope, providesStmt, stmtIndex);
+		findImplementations(this.completionToken, this.unitScope, providesStmt, -1);
 	}
 
 	private void findImplementations(char[] token, Scope scope, ProvidesStatement providesStmt, int stmtIndex) {
-
 		TypeReference theInterface = providesStmt.serviceInterface;
-
 		if (token == null) return;
-		char[][] theInterfaceType = theInterface.getTypeName();
+		char[][] theInterfaceType = null;
+		if (theInterface.resolvedType != null && theInterface.resolvedType.isValidBinding()) {
+			char[] readableName = theInterface.resolvedType.readableName();
+			if (readableName != null)
+				theInterfaceType = CharOperation.splitOn('.', readableName);
+		}
+		theInterfaceType = theInterfaceType == null ? theInterface.getTypeName() : theInterfaceType;
 		if (theInterfaceType == null) return;
 		SearchPattern pattern  = null;
 		NameEnvironmentAnswer answer =  this.nameEnvironment.findTypeInModules(theInterfaceType, scope.module());
@@ -11925,7 +11934,7 @@
 			}
 			@Override
 			public void acceptSearchMatch(SearchMatch match) throws CoreException {
-				checkCancel();
+			//	checkCancel();
 				IJavaElement element = ((IJavaElement) match.getElement());
 				if (element.getElementType() == IJavaElement.TYPE) {
 					IType type = (IType) element;
@@ -11936,7 +11945,9 @@
 						} else {
 							fullTypeName = type.getElementName();
 						}
-						if (!fullTypeName.startsWith(this.prefix) || this.filter.contains(fullTypeName)) return;
+						if (this.filter.contains(fullTypeName)) return;
+						if (!(fullTypeName.startsWith(this.prefix) || type.getElementName().startsWith(this.prefix)))
+							return;
 					}
 					this.types.add(type);
 				}