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();