diff -r b78256795bd4 java.hints/nbproject/project.properties --- a/java.hints/nbproject/project.properties Sat Apr 18 01:04:37 2009 +0200 +++ b/java.hints/nbproject/project.properties Sat Apr 18 16:30:14 2009 +0100 @@ -37,6 +37,12 @@ # Version 2 license, then the option applies only if the new code is # made subject to such option by the copyright holder. +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=true +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=4 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=4 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=4 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=100 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project spec.version.base=1.26.0 javac.source=1.5 diff -r b78256795bd4 java.hints/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFix.java --- a/java.hints/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFix.java Sat Apr 18 01:04:37 2009 +0200 +++ b/java.hints/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFix.java Sat Apr 18 16:30:14 2009 +0100 @@ -77,6 +77,7 @@ import org.openide.filesystems.FileObject; import org.netbeans.modules.java.hints.infrastructure.ErrorHintsProvider; import org.openide.util.NbBundle; +import static org.netbeans.modules.java.hints.errors.Utilities.isEnhancedForLoopIdentifier; /** @@ -167,7 +168,7 @@ working.rewrite(targetTree, result); } else { - if (ErrorFixesFakeHint.isCreateLocalVariableInPlace()) { + if (ErrorFixesFakeHint.isCreateLocalVariableInPlace() || isEnhancedForLoopIdentifier(tp)) { resolveLocalVariable(working, tp, make, proposedType); } else { resolveLocalVariable55(working, tp, make, proposedType); @@ -298,7 +299,9 @@ Tree statementParent = firstUse.getParentPath().getLeaf(); VariableTree vt = make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), name, make.Type(proposedType), null); - if (statementParent.getKind() == Kind.BLOCK) { + if (isEnhancedForLoopIdentifier(tp)) { + wc.rewrite(tp.getParentPath().getLeaf(), vt); + } else if (statementParent.getKind() == Kind.BLOCK) { BlockTree block = (BlockTree) statementParent; FirstUsage fu = new FirstUsage(); diff -r b78256795bd4 java.hints/src/org/netbeans/modules/java/hints/errors/CreateElementUtilities.java --- a/java.hints/src/org/netbeans/modules/java/hints/errors/CreateElementUtilities.java Sat Apr 18 01:04:37 2009 +0200 +++ b/java.hints/src/org/netbeans/modules/java/hints/errors/CreateElementUtilities.java Sat Apr 18 16:30:14 2009 +0100 @@ -93,6 +93,7 @@ import org.netbeans.modules.java.editor.semantic.Utilities; import org.netbeans.modules.java.hints.infrastructure.ErrorHintsProvider; import org.openide.ErrorManager; +import static org.netbeans.modules.java.hints.errors.Utilities.getIterableGenericType; /** * @@ -457,12 +458,22 @@ return Collections.singletonList(info.getTrees().getTypeMirror(new TreePath(parent, vt.getType()))); } - if (vt.getType() == error) { - types.add(ElementKind.CLASS); - return Collections.emptyList(); + TreePath context = parent.getParentPath(); + if (vt.getType() != error || context == null) { + return null; } - return null; + switch (context.getLeaf().getKind()) { + case ENHANCED_FOR_LOOP: + ExpressionTree iterableTree = ((EnhancedForLoopTree) context.getLeaf()).getExpression(); + TreePath iterablePath = new TreePath(context, iterableTree); + TypeMirror type = getIterableGenericType(info, iterablePath); + types.add(ElementKind.LOCAL_VARIABLE); + return Collections.singletonList(type); + default: + types.add(ElementKind.CLASS); + return Collections.emptyList(); + } } private static List computeAssert(Set types, CompilationInfo info, TreePath parent, Tree error, int offset) { diff -r b78256795bd4 java.hints/src/org/netbeans/modules/java/hints/errors/Utilities.java --- a/java.hints/src/org/netbeans/modules/java/hints/errors/Utilities.java Sat Apr 18 01:04:37 2009 +0200 +++ b/java.hints/src/org/netbeans/modules/java/hints/errors/Utilities.java Sat Apr 18 16:30:14 2009 +0100 @@ -50,6 +50,7 @@ import com.sun.source.tree.ParameterizedTypeTree; import com.sun.source.tree.Scope; import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; import com.sun.source.util.TreePath; import java.io.IOException; import java.util.EnumSet; @@ -59,9 +60,11 @@ import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.WildcardType; @@ -154,6 +157,47 @@ return sb.toString(); } + // true if tp is an IDENTIFIER in a VARIABLE in an ENHANCED_FOR_LOOP + public static boolean isEnhancedForLoopIdentifier(TreePath tp) { + if (tp == null || tp.getLeaf().getKind() != Kind.IDENTIFIER) + return false; + TreePath parent = tp.getParentPath(); + if (parent == null || parent.getLeaf().getKind() != Kind.VARIABLE) + return false; + TreePath context = parent.getParentPath(); + if (context == null || context.getLeaf().getKind() != Kind.ENHANCED_FOR_LOOP) + return false; + return true; + } + + // return the generic type of an Iterable (or ArrayType) at a TreePath + public static TypeMirror getIterableGenericType(CompilationInfo info, TreePath iterable) { + TypeElement iterableElement = info.getElements().getTypeElement("java.lang.Iterable"); //NOI18N + if (iterableElement == null) { + return null; + } + TypeMirror iterableType = info.getTrees().getTypeMirror(iterable); + if (iterableType == null) { + return null; + } + TypeMirror designedType = null; + if (iterableType.getKind() == TypeKind.DECLARED) { + DeclaredType declaredType = (DeclaredType) iterableType; + if (!info.getTypes().isSubtype(info.getTypes().erasure(declaredType), info.getTypes().erasure(iterableElement.asType()))) { + return null; + } + ExecutableElement iteratorMethod = (ExecutableElement) iterableElement.getEnclosedElements().get(0); + ExecutableType iteratorMethodType = (ExecutableType) info.getTypes().asMemberOf(declaredType, iteratorMethod); + designedType = ((DeclaredType) iteratorMethodType.getReturnType()).getTypeArguments().get(0); + } else if (iterableType.getKind() == TypeKind.ARRAY) { + designedType = ((ArrayType) iterableType).getComponentType(); + } + if (designedType == null) { + return null; + } + return resolveCapturedType(info, designedType); + } + public static String getName(TypeMirror tm) { if (tm.getKind().isPrimitive()) { return "" + Character.toLowerCase(tm.getKind().name().charAt(0)); diff -r b78256795bd4 java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFixTest.java --- a/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFixTest.java Sat Apr 18 01:04:37 2009 +0200 +++ b/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/AddParameterOrLocalFixTest.java Sat Apr 18 16:30:14 2009 +0100 @@ -137,6 +137,84 @@ "package test; public class Test {public void test() {String foo; {foo = \"bar\";}foo = \"bar\";}}"); } + public void testEnhancedForLoopEmptyList() throws Exception { + parameter = false; + performFixTest("test/Test.java", + "package test;\n" + + "public class Test {\n" + + " public void test() {\n" + + " for (|ttt : java.util.Collections.emptyList()) {}\n" + + " }\n" + + "}\n", + "AddParameterOrLocalFix:ttt:java.lang.Object:false", + ("package test;\n" + + "public class Test {\n" + + " public void test() {\n" + + " for (Object ttt : java.util.Collections.emptyList()) {}\n" + + " }\n" + + "}\n").replaceAll("[ \t\n]+", " ")); + } + + public void testEnhancedForLoopExtendedNumber() throws Exception { + parameter = false; + performFixTest("test/Test.java", + "package test;\n" + + "public class Test {\n" + + " public void test() {\n" + + " java.util.List l = null;\n" + + " for (|ttt : l) {}\n" + + " }\n" + + "}\n", + "AddParameterOrLocalFix:ttt:java.lang.Number:false", + ("package test;\n" + + "public class Test {\n" + + " public void test() {\n" + + " java.util.List l = null;\n" + + " for (Number ttt : l) {}\n" + + " }\n" + + "}\n").replaceAll("[ \t\n]+", " ")); + } + + public void testEnhancedForLoopStringArray() throws Exception { + parameter = false; + performFixTest("test/Test.java", + "package test;\n" + + "public class Test {\n" + + " public void test() {\n" + + " String[] a = null;\n" + + " for (|ttt : a) {}\n" + + " }\n" + + "}\n", + "AddParameterOrLocalFix:ttt:java.lang.String:false", + ("package test;\n" + + "public class Test {\n" + + " public void test() {\n" + + " String[] a = null;\n" + + " for (String ttt : a) {}\n" + + " }\n" + + "}\n").replaceAll("[ \t\n]+", " ")); + } + + public void testEnhancedForLoopPrimitiveArray() throws Exception { + parameter = false; + performFixTest("test/Test.java", + "package test;\n" + + "public class Test {\n" + + " public void test() {\n" + + " int[] a = null;\n" + + " for (|ttt : a) {}\n" + + " }\n" + + "}\n", + "AddParameterOrLocalFix:ttt:int:false", + ("package test;\n" + + "public class Test {\n" + + " public void test() {\n" + + " int[] a = null;\n" + + " for (int ttt : a) {}\n" + + " }\n" + + "}\n").replaceAll("[ \t\n]+", " ")); + } + protected List computeFixes(CompilationInfo info, int pos, TreePath path) throws IOException { List fixes = CreateElement.analyze(info, pos); List result= new LinkedList();