Bug 167472: [quick fix] Suggest to add missing type for for-each variable
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/TypeMismatchQuickFixTests.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/TypeMismatchQuickFixTests.java
index e406ea7..e7ccd00 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/TypeMismatchQuickFixTests.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/TypeMismatchQuickFixTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -2258,6 +2258,40 @@
 		assertExpectedExistInProposals(proposals, expected);
 	}
 
+	public void testTypeMismatchInForEachMissingType() throws Exception {
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("pack", false, null);
+		StringBuffer buf= new StringBuffer();
+		buf.append("package pack;\n");
+		buf.append("\n");
+		buf.append("public class E {\n");
+		buf.append("    public void foo(String[] strings) {\n");
+		buf.append("        for (s: strings) {\n");
+		buf.append("        }\n");
+		buf.append("    }\n");
+		buf.append("}\n");
+		ICompilationUnit cu= pack1.createCompilationUnit("E.java", buf.toString(), false, null);
+		
+		CompilationUnit astRoot= getASTRoot(cu);
+		ArrayList proposals= collectCorrections(cu, astRoot, 3, 2);
+		
+		assertCorrectLabels(proposals);
+		assertNumberOfProposals(proposals, 1);
+		
+		String[] expected= new String[1];
+		buf= new StringBuffer();
+		buf.append("package pack;\n");
+		buf.append("\n");
+		buf.append("public class E {\n");
+		buf.append("    public void foo(String[] strings) {\n");
+		buf.append("        for (String s: strings) {\n");
+		buf.append("        }\n");
+		buf.append("    }\n");
+		buf.append("}\n");
+		expected[0]= buf.toString();
+		
+		assertExpectedExistInProposals(proposals, expected);
+	}
+	
 	public void testNullCheck() throws Exception {
 		IPackageFragment pack1= fSourceFolder.createPackageFragment("pack", false, null);
 		StringBuffer buf= new StringBuffer();
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/UnresolvedTypesQuickFixTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/UnresolvedTypesQuickFixTest.java
index f3c66c6..76a8eeb 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/UnresolvedTypesQuickFixTest.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/UnresolvedTypesQuickFixTest.java
@@ -1552,4 +1552,41 @@
 		assertExpectedExistInProposals(proposals, new String[] { expected1 });
 	}
 
+	public void testForEachMissingType() throws Exception {
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("pack", false, null);
+		StringBuffer buf= new StringBuffer();
+		buf.append("package pack;\n");
+		buf.append("\n");
+		buf.append("import java.util.*;\n");
+		buf.append("\n");
+		buf.append("public class E {\n");
+		buf.append("    public void foo(ArrayList<? extends HashSet<? super Integer>> list) {\n");
+		buf.append("        for (element: list) {\n");
+		buf.append("        }\n");
+		buf.append("    }\n");
+		buf.append("}\n");
+		ICompilationUnit cu= pack1.createCompilationUnit("E.java", buf.toString(), false, null);
+		
+		CompilationUnit astRoot= getASTRoot(cu);
+		ArrayList proposals= collectCorrections(cu, astRoot, 3, 1);
+		
+		assertCorrectLabels(proposals);
+		assertNumberOfProposals(proposals, 6);
+		
+		String[] expected= new String[1];
+		buf= new StringBuffer();
+		buf.append("package pack;\n");
+		buf.append("\n");
+		buf.append("import java.util.*;\n");
+		buf.append("\n");
+		buf.append("public class E {\n");
+		buf.append("    public void foo(ArrayList<? extends HashSet<? super Integer>> list) {\n");
+		buf.append("        for (HashSet<? super Integer> element: list) {\n");
+		buf.append("        }\n");
+		buf.append("    }\n");
+		buf.append("}\n");
+		expected[0]= buf.toString();
+		
+		assertExpectedExistInProposals(proposals, expected);
+	}
 }
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/ASTResolving.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/ASTResolving.java
index 9cc639b..db17f58 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/ASTResolving.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/ASTResolving.java
@@ -880,6 +880,8 @@
 				int superParent= parent.getParent().getNodeType();
 				if (superParent == ASTNode.CATCH_CLAUSE) {
 					kind= SimilarElementsRequestor.CLASSES;
+				} else if (superParent == ASTNode.ENHANCED_FOR_STATEMENT) {
+					kind= SimilarElementsRequestor.REF_TYPES;
 				}
 				break;
 			case ASTNode.TAG_ELEMENT:
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java
index 37f325c..5df46e8 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java
@@ -118,6 +118,7 @@
 	public static String TypeMismatchSubProcessor_changereturntype_description;
 	public static String TypeMismatchSubProcessor_changereturnofoverridden_description;
 	public static String TypeMismatchSubProcessor_changereturnofimplemented_description;
+	public static String TypeMismatchSubProcessor_create_loop_variable_description;
 	public static String TypeMismatchSubProcessor_removeexceptions_description;
 	public static String TypeMismatchSubProcessor_addexceptions_description;
 	public static String TypeMismatchSubProcessor_incompatible_for_each_type_description;
@@ -173,6 +174,7 @@
 	public static String UnresolvedElementsSubProcessor_changemethod_description;
 	public static String UnresolvedElementsSubProcessor_changetoouter_description;
 	public static String UnresolvedElementsSubProcessor_changetomethod_description;
+	public static String UnresolvedElementsSubProcessor_create_loop_variable_description;
 	public static String UnresolvedElementsSubProcessor_createmethod_description;
 	public static String UnresolvedElementsSubProcessor_createmethod_other_description;
 	public static String UnresolvedElementsSubProcessor_createconstructor_description;
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties
index df5ddb3..b7c7ac6 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties
@@ -122,6 +122,7 @@
 TypeMismatchSubProcessor_changereturntype_description=Change method return type to ''{0}''
 TypeMismatchSubProcessor_changereturnofoverridden_description=Change return type of overridden ''{0}(..)''
 TypeMismatchSubProcessor_changereturnofimplemented_description=Change return type of implemented ''{0}(..)''
+TypeMismatchSubProcessor_create_loop_variable_description=Create loop variable ''{0}''
 TypeMismatchSubProcessor_removeexceptions_description=Remove exceptions from ''{0}(..)''
 TypeArgumentMismatchSubProcessor_removeTypeArguments=Remove type arguments
 TypeMismatchSubProcessor_addexceptions_description=Add exceptions to ''{0}.{1}(..)''
@@ -189,6 +190,7 @@
 UnresolvedElementsSubProcessor_changemethod_description=Change to ''{0}(..)''
 UnresolvedElementsSubProcessor_changetoouter_description=Qualify with enclosing type ''{0}''
 UnresolvedElementsSubProcessor_changetomethod_description=Change to ''{0}''
+UnresolvedElementsSubProcessor_create_loop_variable_description=Create loop variable ''{0}''
 UnresolvedElementsSubProcessor_createmethod_description=Create method ''{0}''
 UnresolvedElementsSubProcessor_createmethod_other_description=Create method ''{0}'' in type ''{1}''
 UnresolvedElementsSubProcessor_createconstructor_description=Create constructor ''{0}''
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/TypeMismatchSubProcessor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/TypeMismatchSubProcessor.java
index 29c1831..f904ad5 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/TypeMismatchSubProcessor.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/TypeMismatchSubProcessor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -39,6 +39,8 @@
 import org.eclipse.jdt.core.dom.MemberValuePair;
 import org.eclipse.jdt.core.dom.MethodDeclaration;
 import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
 import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
 import org.eclipse.jdt.core.dom.Type;
@@ -48,6 +50,7 @@
 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
 
 import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
+import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
 import org.eclipse.jdt.internal.corext.dom.Bindings;
 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
@@ -67,6 +70,7 @@
 import org.eclipse.jdt.internal.ui.text.correction.proposals.ChangeMethodSignatureProposal.RemoveDescription;
 import org.eclipse.jdt.internal.ui.text.correction.proposals.ImplementInterfaceProposal;
 import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedCorrectionProposal;
+import org.eclipse.jdt.internal.ui.text.correction.proposals.NewVariableCorrectionProposal;
 import org.eclipse.jdt.internal.ui.text.correction.proposals.TypeChangeCorrectionProposal;
 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
 import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
@@ -450,10 +454,26 @@
 
 		SingleVariableDeclaration parameter= forStatement.getParameter();
 
+		ICompilationUnit cu= context.getCompilationUnit();
+		if (parameter.getName().getLength() == 0
+				&& parameter.getType() instanceof SimpleType) {
+			SimpleType type= (SimpleType) parameter.getType();
+			if (type.getName() instanceof SimpleName) {
+				SimpleName simpleName= (SimpleName) type.getName();
+				String name= simpleName.getIdentifier();
+				int relevance= StubUtility.hasLocalVariableName(cu.getJavaProject(), name) ? 10 : 7;
+				String label= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_create_loop_variable_description, BasicElementLabels.getJavaElementName(name));
+				Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_LOCAL);
+				
+				proposals.add(new NewVariableCorrectionProposal(label, cu, NewVariableCorrectionProposal.LOCAL, simpleName, null, relevance, image));
+				return;
+			}
+		}
+		
 		String label= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_incompatible_for_each_type_description, new String[] { BasicElementLabels.getJavaElementName(parameter.getName().getIdentifier()), BindingLabelProvider.getBindingLabel(expectedBinding, BindingLabelProvider.DEFAULT_TEXTFLAGS) });
 		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
 		ASTRewrite rewrite= ASTRewrite.create(ast);
-		ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.INCOMPATIBLE_FOREACH_TYPE, image);
+		ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, cu, rewrite, IProposalRelevance.INCOMPATIBLE_FOREACH_TYPE, image);
 
 		ImportRewrite importRewrite= proposal.createImportRewrite(astRoot);
 		ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(ASTResolving.findParentBodyDeclaration(selectedNode), importRewrite);
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/UnresolvedElementsSubProcessor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/UnresolvedElementsSubProcessor.java
index a877736..2dcf18c 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/UnresolvedElementsSubProcessor.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/UnresolvedElementsSubProcessor.java
@@ -81,6 +81,7 @@
 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
 import org.eclipse.jdt.core.dom.CompilationUnit;
 import org.eclipse.jdt.core.dom.ConstructorInvocation;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
 import org.eclipse.jdt.core.dom.Expression;
 import org.eclipse.jdt.core.dom.FieldAccess;
 import org.eclipse.jdt.core.dom.IBinding;
@@ -100,6 +101,7 @@
 import org.eclipse.jdt.core.dom.SimpleName;
 import org.eclipse.jdt.core.dom.SimpleType;
 import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
 import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
 import org.eclipse.jdt.core.dom.SuperFieldAccess;
@@ -595,6 +597,10 @@
 		}
 
 		int kind= evauateTypeKind(selectedNode, cu.getJavaProject());
+		
+		if (kind == SimilarElementsRequestor.REF_TYPES) {
+			addEnhancedForWithoutTypeProposals(cu, selectedNode, proposals);
+		}
 
 		while (selectedNode.getLocationInParent() == QualifiedName.NAME_PROPERTY) {
 			selectedNode= selectedNode.getParent();
@@ -634,6 +640,26 @@
 		ReorgCorrectionsSubProcessor.addProjectSetupFixProposal(context, problem, node.getFullyQualifiedName(), proposals);
 	}
 
+	private static void addEnhancedForWithoutTypeProposals(ICompilationUnit cu, ASTNode selectedNode, Collection<ICommandAccess> proposals) {
+		if (selectedNode instanceof SimpleName && selectedNode.getLocationInParent() == SimpleType.NAME_PROPERTY) {
+			ASTNode type= selectedNode.getParent();
+			if (type.getLocationInParent() == SingleVariableDeclaration.TYPE_PROPERTY) {
+				SingleVariableDeclaration svd= (SingleVariableDeclaration) type.getParent();
+				if (svd.getLocationInParent() == EnhancedForStatement.PARAMETER_PROPERTY) {
+					if (svd.getName().getLength() == 0) {
+						SimpleName simpleName= (SimpleName) selectedNode;
+						String name= simpleName.getIdentifier();
+						int relevance= StubUtility.hasLocalVariableName(cu.getJavaProject(), name) ? 10 : 7;
+						String label= Messages.format(CorrectionMessages.UnresolvedElementsSubProcessor_create_loop_variable_description, BasicElementLabels.getJavaElementName(name));
+						Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_LOCAL);
+						
+						proposals.add(new NewVariableCorrectionProposal(label, cu, NewVariableCorrectionProposal.LOCAL, simpleName, null, relevance, image));
+					}
+				}
+			}
+		}
+	}
+
 	private static void addNullityAnnotationTypesProposals(ICompilationUnit cu, Name node, Collection<ICommandAccess> proposals) throws CoreException {
 		ASTNode parent= node.getParent();
 		boolean isAnnotationName= parent instanceof Annotation && ((Annotation) parent).getTypeNameProperty() == node.getLocationInParent();
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposal.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposal.java
index 27e7347..d8f2488 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposal.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposal.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -31,6 +31,7 @@
 import org.eclipse.jdt.core.dom.BodyDeclaration;
 import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
 import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
 import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
 import org.eclipse.jdt.core.dom.EnumDeclaration;
 import org.eclipse.jdt.core.dom.Expression;
@@ -196,6 +197,16 @@
 	}
 
 
+	private boolean isEnhancedForStatementVariable(Statement statement, SimpleName name) {
+		if (statement instanceof EnhancedForStatement) {
+			EnhancedForStatement forStatement= (EnhancedForStatement) statement;
+			SingleVariableDeclaration param= forStatement.getParameter();
+			return param.getType() == name.getParent(); // strange recovery, see https://bugs.eclipse.org/180456
+		}
+		return false;
+	}
+	
+	
 	private ASTRewrite doAddLocal(CompilationUnit cu) {
 		AST ast= cu.getAST();
 
@@ -267,7 +278,50 @@
 			setEndPosition(rewrite.track(expression));
 
 			return rewrite;
+			
+		} else if ((dominant != dominantStatement) && isEnhancedForStatementVariable(dominantStatement, node)) {
+			//	for (x: collectionOfT) -> for (T x: collectionOfT)
+			
+			EnhancedForStatement enhancedForStatement= (EnhancedForStatement) dominantStatement;
+			SingleVariableDeclaration parameter= enhancedForStatement.getParameter();
+			Expression expression= enhancedForStatement.getExpression();
+			
+			SimpleName newName= (SimpleName) rewrite.createMoveTarget(node);
+			rewrite.set(parameter, SingleVariableDeclaration.NAME_PROPERTY, newName, null);
+			
+			ITypeBinding elementBinding= null;
+			ITypeBinding typeBinding= expression.resolveTypeBinding();
+			if (typeBinding != null) {
+				if (typeBinding.isArray()) {
+					elementBinding= typeBinding.getElementType();
+				} else {
+					ITypeBinding iterable= Bindings.findTypeInHierarchy(typeBinding, "java.lang.Iterable"); //$NON-NLS-1$
+					if (iterable != null) {
+						ITypeBinding[] typeArguments= iterable.getTypeArguments();
+						if (typeArguments.length == 1) {
+							elementBinding= typeArguments[0];
+							elementBinding= Bindings.normalizeForDeclarationUse(elementBinding, ast);
+						}
+					}
+				}
+			}
+			Type type;
+			if (elementBinding != null) {
+				type= imports.addImport(elementBinding, ast, importRewriteContext);
+			} else {
+				type= ast.newSimpleType(ast.newSimpleName("Object")); //$NON-NLS-1$
+			}
+
+			rewrite.set(parameter, SingleVariableDeclaration.TYPE_PROPERTY, type, null);
+			
+			addLinkedPosition(rewrite.track(type), false, KEY_TYPE);
+			addLinkedPosition(rewrite.track(newName), true, KEY_NAME);
+			
+			setEndPosition(rewrite.track(expression));
+			
+			return rewrite;
 		}
+		
 		//	foo(x) -> int x; foo(x)
 
 		VariableDeclarationFragment newDeclFrag= ast.newVariableDeclarationFragment();