This is an automated email from the ASF dual-hosted git repository. jlahoda pushed a commit to branch release90 in repository https://gitbox.apache.org/repos/asf/incubator-netbeans.git
commit 8d2efa08796eef39503897cddee9d3b897af8b12 Author: Arunava Sinha <[email protected]> AuthorDate: Mon Apr 23 20:21:40 2018 +0530 netbeans-481: Added new ErrorRule to fix compiler error on initialization of var type variable with array --- .../ConvertInvalidVarToExplicitArrayType.java | 197 ++++++++++++++++++++ .../modules/java/hints/resources/layer.xml | 1 + .../ConvertInvalidVarToExplicitArrayTypeTest.java | 201 +++++++++++++++++++++ .../modules/java/source/save/CasualDiff.java | 72 +++++++- .../api/java/source/SourceUtilsTestUtil.java | 11 +- 5 files changed, 478 insertions(+), 4 deletions(-) diff --git a/java.hints/src/org/netbeans/modules/java/hints/errors/ConvertInvalidVarToExplicitArrayType.java b/java.hints/src/org/netbeans/modules/java/hints/errors/ConvertInvalidVarToExplicitArrayType.java new file mode 100644 index 0000000..d8de06b --- /dev/null +++ b/java.hints/src/org/netbeans/modules/java/hints/errors/ConvertInvalidVarToExplicitArrayType.java @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.hints.errors; + +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.NewArrayTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.Scope; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import com.sun.source.tree.VariableTree; +import java.util.HashMap; +import java.util.Map; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.TreeMaker; +import org.netbeans.api.java.source.WorkingCopy; +import org.netbeans.modules.java.hints.spi.ErrorRule; +import org.netbeans.spi.editor.hints.Fix; +import org.netbeans.spi.java.hints.JavaFix; +import org.openide.util.NbBundle.Messages; + +/** + * + * @author arusinha + */ +@Messages({ + "DN_ConvertVarToExplicitType=Convert Var to Explicit Type" +}) +public class ConvertInvalidVarToExplicitArrayType implements ErrorRule<Void> { + + private static final Set<String> CODES; + private static final Map<TypeKind, Integer> NUMERIC_PRIMITIVE_TYPE_PRIORITY_MAP; + + static { + Set<String> codes = new HashSet<>(); + codes.add("compiler.err.cant.infer.local.var.type"); // NOI18N + CODES = Collections.unmodifiableSet(codes); + + Map<TypeKind, Integer> map = new HashMap<>(); + map.put(TypeKind.BYTE, 1); + map.put(TypeKind.SHORT, 2); + map.put(TypeKind.INT, 3); + map.put(TypeKind.LONG, 4); + map.put(TypeKind.FLOAT, 5); + map.put(TypeKind.DOUBLE, 6); + NUMERIC_PRIMITIVE_TYPE_PRIORITY_MAP = Collections.unmodifiableMap(map); + } + + @Override + public Set<String> getCodes() { + return CODES; + } + + @Override + public List<Fix> run(CompilationInfo compilationInfo, String diagnosticKey, int offset, TreePath treePath, Data<Void> data) { + + if (treePath.getParentPath() == null) { + return null; + } + + TypeMirror arrayTypeMirror = null; + if (treePath.getLeaf().getKind() == Tree.Kind.VARIABLE) { + VariableTree oldVariableTree = (VariableTree) treePath.getLeaf(); + NewArrayTree arrayTree = (NewArrayTree) oldVariableTree.getInitializer(); + List<? extends ExpressionTree> currentValues = arrayTree.getInitializers(); + + TypeMirror arrayElementTypeMirror = null; + Integer maxTypePriority = -1; + + boolean isHomoGeneousArray = true; + for (ExpressionTree tree : currentValues) { + + Scope s = compilationInfo.getTrees().getScope(treePath); + + arrayElementTypeMirror = compilationInfo.getTreeUtilities().attributeTree(tree, s); + + if (tree instanceof NewClassTree) { + NewClassTree nct = (NewClassTree) tree; + if (nct.getIdentifier().getKind() == Tree.Kind.PARAMETERIZED_TYPE) { + + return null; + } + } + + if (arrayTypeMirror == null) { + arrayTypeMirror = arrayElementTypeMirror; + } + + isHomoGeneousArray = compilationInfo.getTypes().isSameType(arrayElementTypeMirror, arrayTypeMirror); + if (isHomoGeneousArray) { + continue; + } + + // Hint will be enabled only for primitive Numeric array initializers or for homogeneous array members. + if (!isHomoGeneousArray && !isPrimitiveNumeric(arrayElementTypeMirror.getKind())) { + return null; + } + + if (!isHomoGeneousArray) { + Integer arrayElementPriority = NUMERIC_PRIMITIVE_TYPE_PRIORITY_MAP.get(arrayElementTypeMirror.getKind()); + if (maxTypePriority == -1) { + maxTypePriority = NUMERIC_PRIMITIVE_TYPE_PRIORITY_MAP.get(arrayTypeMirror.getKind()); + } + + if (maxTypePriority < arrayElementPriority) { + arrayTypeMirror = arrayElementTypeMirror; + maxTypePriority = arrayElementPriority; + } + } + } + } + return Collections.<Fix>singletonList(new FixImpl(compilationInfo, treePath, arrayTypeMirror).toEditorFix()); + + } + + @Override + public String getId() { + return ConvertInvalidVarToExplicitArrayType.class.getName(); + } + + @Override + public String getDisplayName() { + return Bundle.DN_ConvertVarToExplicitType(); + } + + @Override + public void cancel() { + } + + private static final class FixImpl extends JavaFix { + + private TypeMirror arrayTypeMirror; + + public FixImpl(CompilationInfo info, TreePath tp, TypeMirror arrayType) { + super(info, tp); + this.arrayTypeMirror = arrayType; + } + + @Override + protected String getText() { + return Bundle.DN_ConvertVarToExplicitType(); + } + + @Override + protected void performRewrite(TransformationContext tc) throws Exception { + WorkingCopy wc = tc.getWorkingCopy(); + wc.toPhase(JavaSource.Phase.RESOLVED); + TreePath statementPath = tc.getPath(); + TreeMaker make = wc.getTreeMaker(); + VariableTree oldVariableTree = null; + + if (statementPath.getLeaf().getKind() == Tree.Kind.VARIABLE) { + oldVariableTree = (VariableTree) statementPath.getLeaf(); + + arrayTypeMirror = Utilities.resolveCapturedType(wc, arrayTypeMirror); + + VariableTree newVariableTree = make.Variable( + oldVariableTree.getModifiers(), + oldVariableTree.getName(), + make.ArrayType(make.Type(arrayTypeMirror)), + oldVariableTree.getInitializer() + ); + tc.getWorkingCopy().rewrite(oldVariableTree, newVariableTree); + } + } + } + + private boolean isPrimitiveNumeric(TypeKind type) { + Set<TypeKind> primitiveNumbericTypes = NUMERIC_PRIMITIVE_TYPE_PRIORITY_MAP.keySet(); + + return primitiveNumbericTypes.contains(type); + + } +} diff --git a/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml b/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml index 6f98b8f..aa9d4e9 100644 --- a/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml +++ b/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml @@ -182,6 +182,7 @@ <file name="org-netbeans-modules-java-hints-errors-ExtraCatch.instance"/> <file name="org-netbeans-modules-java-hints-errors-TypeErroneous.instance"/> <file name="org-netbeans-modules-java-hints-project-IncompleteClassPath.instance"/> + <file name="org-netbeans-modules-java-hints-errors-ConvertInvalidVarToExplicitArrayType.instance"/> <folder name="text"> <folder name="x-jsp"> <file name="org-netbeans-modules-java-hints-errors-ImportClass.instance"/> diff --git a/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/ConvertInvalidVarToExplicitArrayTypeTest.java b/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/ConvertInvalidVarToExplicitArrayTypeTest.java new file mode 100644 index 0000000..5128e2e --- /dev/null +++ b/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/ConvertInvalidVarToExplicitArrayTypeTest.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.hints.errors; + +import com.sun.source.util.TreePath; +import java.util.LinkedList; +import java.util.List; +import org.netbeans.modules.java.hints.infrastructure.ErrorHintsTestBase; +import org.netbeans.spi.editor.hints.Fix; +import org.openide.util.NbBundle; +import org.netbeans.modules.java.source.parsing.JavacParser; +import org.netbeans.api.java.source.CompilationInfo; + +/** + * + * @author arusinha + */ +public class ConvertInvalidVarToExplicitArrayTypeTest extends ErrorHintsTestBase { + + public ConvertInvalidVarToExplicitArrayTypeTest(String name) throws Exception { + super(name, ConvertInvalidVarToExplicitArrayType.class); + } + + @Override + protected void tearDown() throws Exception { + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = false; + super.tearDown(); + } + + public void testArrayHetrogeneousElements() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performAnalysisTest("test/Test.java", "package test; public class Test {{var/*comment1*/ k = {1,'c'};}}", -1); + } + + public void testParameterizedElements() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performAnalysisTest("test/Test.java", + "package test; public class Test {{final var j = {new java.util.ArrayList<String>(),new java.util.ArrayList<String>()};}}", + -1); + } + + public void testArrayObjectElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{final/*comment1*/ var/**comment2**/ j/*comment3*/ = /*comment4*/{new java.util.ArrayList(),new java.util.ArrayList()};}}", + -1, "Convert Var to Explicit Type", + "package test; import java.util.ArrayList; public class Test {{final/*comment1*/ ArrayList[]/**comment2**/ j/*comment3*/ = /*comment4*/{new java.util.ArrayList(),new java.util.ArrayList()};}}"); + } + + public void testArrayPrimitiveNumericElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{final var j = {1,2.1,3f};}}", + -1, + "Convert Var to Explicit Type", + "package test; public class Test {{final double[] j = {1,2.1,3f};}}"); + } + + public void testArrayPrimitiveNumeric2ElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{final var j = {(short)1,(byte)2};}}", + -1, + "Convert Var to Explicit Type", + "package test; public class Test {{final short[] j = {(short)1,(byte)2};}}"); + } + + public void testArrayStringElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{/*comment1*/ /*comment2*/@NotNull final var j = {\"hello\",\"world\"};}}", + -1, + "Convert Var to Explicit Type", + "package test; public class Test {{/*comment1*/ /*comment2*/@NotNull final String[] j = {\"hello\",\"world\"};}}"); + } + + public void testArrayObject1ElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{@NotNull final var j = {new Object(),new Object()};}}", + -1, + "Convert Var to Explicit Type", + "package test; public class Test {{@NotNull final Object[] j = {new Object(),new Object()};}}"); + } + + public void testArrayObject2ElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{@NotNull var j = {new Object(),new Object()};}}", + -1, + "Convert Var to Explicit Type", + "package test; public class Test {{@NotNull Object[] j = {new Object(),new Object()};}}"); + } + + public void testArrayObject3ElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{final @NotNull var j = {new Object(),new Object()};}}", + -1, + "Convert Var to Explicit Type", + "package test; public class Test {{final @NotNull Object[] j = {new Object(),new Object()};}}"); + } + + public void testArrayObject4ElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{final/*comment1*/var a = {new Object(),new Object()};}}", + -1, + "Convert Var to Explicit Type", + "package test; public class Test {{final/*comment1*/Object[] a = {new Object(),new Object()};}}"); + } + + public void testArrayObject5ElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{final/*comment1*/var /*comment2*/ a = {2,3.1f};}}", + -1, + "Convert Var to Explicit Type", + "package test; public class Test {{final/*comment1*/float[] /*comment2*/ a = {2,3.1f};}}"); + } + + public void testArrayObject6ElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{/*comment1*/var/*comment2*/ a = {2,3.1f};}}", + -1, + "Convert Var to Explicit Type", + "package test; public class Test {{/*comment1*/float[]/*comment2*/ a = {2,3.1f};}}"); + } + + public void testArrayObject7ElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{var/*comment1*/ a = {2,3.1f};}}", + -1, + "Convert Var to Explicit Type", + "package test; public class Test {{float[]/*comment1*/ a = {2,3.1f};}}"); + } + + public void testArrayObject8ElementsFix() throws Exception { + sourceLevel = "1.10"; + JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true; + performFixTest("test/Test.java", + "package test; public class Test {{@NotNull var j = {new Object(),new Object()};}}", + -1, + "Convert Var to Explicit Type", + "package test; public class Test {{@NotNull Object[] j = {new Object(),new Object()};}}"); + } + + protected List<Fix> computeFixes(CompilationInfo info, int pos, TreePath path) { + List<Fix> fixes = new ConvertInvalidVarToExplicitArrayType().run(info, null, pos, path, null); + List<Fix> result = new LinkedList<Fix>(); + + for (Fix f : fixes) { + if (f instanceof Fix) { + result.add(f); + } + } + + return result; + } + + @Override + protected String toDebugString(CompilationInfo info, Fix f) { + return (f.getText()); + } + + static { + NbBundle.setBranding("test"); + } + +} diff --git a/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java b/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java index 4cef162..933af52 100644 --- a/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java +++ b/java.source.base/src/org/netbeans/modules/java/source/save/CasualDiff.java @@ -166,6 +166,7 @@ import org.netbeans.modules.java.source.transform.FieldGroupTree; import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.NbCollections; +import javax.lang.model.type.TypeKind; public class CasualDiff { @@ -1469,8 +1470,77 @@ public class CasualDiff { addDimensions = dimension(newT.vartype, -1); cLikeArray = vartypeBounds[1] > oldT.pos; cLikeArrayChange = cLikeArray && dimension(oldT.vartype, oldT.pos) > addDimensions; - copyTo(localPointer, vartypeBounds[0]); + + /** + * Extracting modifier from oldTree using symbol position and + * modifier positions when oldT.type is error and vartype + * upperbound is not proper. + */ + if (oldT.type.getKind() == TypeKind.ERROR && vartypeBounds[1] == -1) { + + // returns -1 if modifiers not present. + int modsUpperBound = getCommentCorrectedEndPos(oldT.mods); + if (modsUpperBound > -1) { + tokenSequence.move(modsUpperBound); + tokenSequence.moveNext(); + + // copying modifiers from oldTree + if (JavaTokenId.WHITESPACE == tokenSequence.token().id()) { + int offset = tokenSequence.offset(); + copyTo(localPointer, localPointer = offset); + } else { + copyTo(localPointer, localPointer = modsUpperBound); + } + } + + tokenSequence.move(localPointer); + tokenSequence.moveNext(); + int offset = tokenSequence.offset(); + JavaTokenId tokenId = tokenSequence.token().id(); + + //adding back all whitespaces/block-comment/javadoc-comments present in OldTree before variable type token. + while ((tokenId == JavaTokenId.WHITESPACE || tokenId == JavaTokenId.BLOCK_COMMENT || tokenId == JavaTokenId.JAVADOC_COMMENT) && offset < oldT.sym.pos) { + printer.print(tokenSequence.token().text().toString()); + tokenSequence.moveNext(); + offset = tokenSequence.offset(); + tokenId = tokenSequence.token().id(); + } + + // Correcting lower/upper bounds for oldT.vartype tree. + vartypeBounds[1] = oldT.sym.pos; + vartypeBounds[0] = offset; + + } else { + copyTo(localPointer, vartypeBounds[0]); + + } + localPointer = diffTree(oldT.vartype, newT.vartype, vartypeBounds); + + /** + * For erroneous variable type sometime diffTree function return + * wrong position. In that scenario only old variable type will + * be replaced with new variable type leaving out succeeding + * comments tokens present(if any). Below code copies successive + * tokens after excluding variable type token which will be the + * first token. + */ + if (oldT.type.getKind() == TypeKind.ERROR && localPointer == -1) { + + // moving to variable type token + tokenSequence.move(vartypeBounds[0]); + tokenSequence.moveNext(); + + //moving to first token after variable type token. + tokenSequence.moveNext(); + + // copying tokens from vartype bounds after excluding variable type token. + int offset = tokenSequence.offset(); + if (offset < vartypeBounds[1]) { + copyTo(offset, vartypeBounds[1]); + } + localPointer = vartypeBounds[1]; + } } } if (nameChanged(oldT.name, newT.name)) { diff --git a/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTestUtil.java b/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTestUtil.java index 8768ba0..f8214d5 100644 --- a/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTestUtil.java +++ b/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTestUtil.java @@ -40,6 +40,7 @@ import org.netbeans.api.java.source.JavaSource.Phase; import org.netbeans.editor.BaseDocument; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.java.JavaDataLoader; +import org.netbeans.modules.java.source.BootClassPathUtil; import org.netbeans.modules.java.source.TestUtil; import org.netbeans.modules.java.source.indexing.JavaCustomIndexer; import org.netbeans.modules.java.source.parsing.JavacParser; @@ -280,9 +281,13 @@ public final class SourceUtilsTestUtil extends ProxyLookup { public ClassPath findClassPath(FileObject file, String type) { try { - if (ClassPath.BOOT == type || JavaClassPathConstants.MODULE_BOOT_PATH.equals(type)) { - return ClassPathSupport.createClassPath(getBootClassPath().toArray(new URL[0])); - } + if (ClassPath.BOOT == type) { + return ClassPathSupport.createClassPath(getBootClassPath().toArray(new URL[0])); + } + + if (JavaClassPathConstants.MODULE_BOOT_PATH == type) { + return BootClassPathUtil.getModuleBootPath(); + } if (ClassPath.SOURCE == type) { return ClassPathSupport.createClassPath(new FileObject[] { -- To stop receiving notification emails like this one, please contact [email protected]. --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected] For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists
