This is an automated email from the ASF dual-hosted git repository. geertjan pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-netbeans.git
The following commit(s) were added to refs/heads/master by this push: new a43f54b [NETBEANS-498] JDK10-LVTI: Provide fix hint to convert var to explicit type (#509) a43f54b is described below commit a43f54baae947db08387a61e2416724355f38dc2 Author: Reema Taneja <32299405+rtane...@users.noreply.github.com> AuthorDate: Sat May 12 00:00:42 2018 +0530 [NETBEANS-498] JDK10-LVTI: Provide fix hint to convert var to explicit type (#509) * [NETBEANS-498] Provide fix hint to convert var to explicit type * Corrected license info * corrected hint related display strings * addressed review comments * Incorporated review comments, prevent conversion of intersection types * moved version check outside of TreeUtilities api * minor change * Replaced redudant check with specific check for Intersection type --- .../modules/java/hints/jdk/Bundle.properties | 2 + .../java/hints/jdk/ConvertVarToExplicitType.java | 170 ++++++++++ .../hints/jdk/ConvertVarToExplicitTypeTest.java | 355 +++++++++++++++++++++ .../modules/java/source/save/CasualDiff.java | 30 +- 4 files changed, 555 insertions(+), 2 deletions(-) diff --git a/java.hints/src/org/netbeans/modules/java/hints/jdk/Bundle.properties b/java.hints/src/org/netbeans/modules/java/hints/jdk/Bundle.properties index df35377..6491679 100644 --- a/java.hints/src/org/netbeans/modules/java/hints/jdk/Bundle.properties +++ b/java.hints/src/org/netbeans/modules/java/hints/jdk/Bundle.properties @@ -102,3 +102,5 @@ DESC_ConvertIfToSwitch_EmptyDefault=If checked, the hint will generate an empty DN_CanUseVarForExplicitType=Convert Explicit Type to Var DESC_CanUseVarForExplicitType=Converts explicit type of local variable to var. +DN_ConvertVarToExplicitType=Convert Var to Explicit Type +DESC_ConvertVarToExplicitType=Converts var type local variable to explicit type. diff --git a/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertVarToExplicitType.java b/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertVarToExplicitType.java new file mode 100644 index 0000000..9a09945 --- /dev/null +++ b/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertVarToExplicitType.java @@ -0,0 +1,170 @@ +/* + * 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.jdk; + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreePath; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.ElementKind; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic; +import javax.tools.Diagnostic.Kind; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.TreeMaker; +import org.netbeans.api.java.source.WorkingCopy; +import org.netbeans.modules.java.hints.errors.Utilities; +import org.netbeans.spi.editor.hints.ErrorDescription; +import org.netbeans.spi.java.hints.ErrorDescriptionFactory; +import org.netbeans.spi.java.hints.Hint; +import org.netbeans.spi.java.hints.HintContext; +import org.netbeans.spi.java.hints.JavaFix; +import org.netbeans.spi.java.hints.JavaFix.TransformationContext; +import org.netbeans.spi.java.hints.TriggerPattern; +import org.openide.util.NbBundle.Messages; + +/** + * Hint to convert type in local variable declaration from 'var' to explicit + * type + * + * @author rtaneja + */ +@Hint(displayName = "#DN_ConvertVarToExplicitType", description = "#DESC_ConvertVarToExplicitType", category = "rules15", minSourceVersion = "10") +@Messages("MSG_ConvertibleToExplicitType=Convert var to explicit type") +public class ConvertVarToExplicitType { + + @TriggerPattern("$mods$ $type $var = $init") //NOI18N + public static ErrorDescription convertVarToExplicitType(HintContext ctx) { + + if (!isLocalVarType(ctx)) { + return null; + } + TreePath treePath = ctx.getPath(); + if (hasDiagnosticErrors(ctx.getInfo(), treePath.getLeaf())) { + return null; + } + + if (!isValidType(ctx)) { + return null; + } + + return ErrorDescriptionFactory.forTree(ctx, ctx.getPath(), Bundle.MSG_ConvertibleToExplicitType(), + new JavaFixImpl(ctx.getInfo(), ctx.getPath()).toEditorFix()); + } + + /** + * Fix for converting local 'var' type to explicit variable type + * + */ + private static final class JavaFixImpl extends JavaFix { + + public JavaFixImpl(CompilationInfo info, TreePath tp) { + super(info, tp); + } + + @Override + @Messages("FIX_convertVarToExplicitType=Replace var with explicit type") + protected String getText() { + return Bundle.FIX_convertVarToExplicitType(); + } + + @Override + protected void performRewrite(TransformationContext tc) throws Exception { + WorkingCopy wc = tc.getWorkingCopy(); + CompilationUnitTree cut = wc.getCompilationUnit(); + TreePath statementPath = tc.getPath(); + + TreeMaker make = wc.getTreeMaker(); + + if (statementPath.getLeaf().getKind() == Tree.Kind.VARIABLE) { + VariableTree oldVariableTree = (VariableTree) statementPath.getLeaf(); + TypeMirror type = wc.getTrees().getTypeMirror(statementPath); + VariableTree newVariableTree = make.Variable( + oldVariableTree.getModifiers(), + oldVariableTree.getName(), + make.Type(type), + oldVariableTree.getInitializer() + ); + wc.rewrite(oldVariableTree, newVariableTree); + } + } + + } + + /** + * + * @param ctx : HintContext + * @return true if pre-conditions for hint to be enable is meet + */ + private static boolean isLocalVarType(HintContext ctx) { + + CompilationInfo info = ctx.getInfo(); + + if (info.getSourceVersion().compareTo(SourceVersion.RELEASE_9) < 1) { + return false; + } + + TreePath treePath = ctx.getPath(); + + // should be local variable + if (info.getTrees().getElement(treePath).getKind() != ElementKind.LOCAL_VARIABLE) { + return false; + } + + // variable declaration of type 'var' + return info.getTreeUtilities().isVarType(treePath); + } + + private static boolean hasDiagnosticErrors(CompilationInfo info, Tree tree) { + long startPos = info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), tree); + long endPos = info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), tree); + + for (Diagnostic<?> d : info.getDiagnostics()) { + if (d.getKind() == Kind.ERROR) { + if ((d.getPosition() >= startPos) && (d.getPosition() <= endPos)) { + return true; + } + } + } + return false; + } + + //filter anonymous class and intersection types + private static boolean isValidType(HintContext ctx) { + TreePath treePath = ctx.getPath(); + TreePath initTreePath = ctx.getVariables().get("$init"); //NOI18N + + if (initTreePath.getLeaf().getKind() == Tree.Kind.NEW_CLASS) { + NewClassTree nct = ((NewClassTree) initTreePath.getLeaf()); + if (nct.getClassBody() != null) { + return false; + } + } + + TypeMirror variableTypeMirror = ctx.getInfo().getTrees().getElement(treePath).asType(); + + if (!Utilities.isValidType(variableTypeMirror) ||(variableTypeMirror.getKind() == TypeKind.INTERSECTION)) { + return false; + } + return true; + } +} diff --git a/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertVarToExplicitTypeTest.java b/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertVarToExplicitTypeTest.java new file mode 100644 index 0000000..3942ee7 --- /dev/null +++ b/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertVarToExplicitTypeTest.java @@ -0,0 +1,355 @@ +/* + * 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.jdk; + +import org.junit.Test; +import org.netbeans.modules.java.hints.test.api.HintTest; + +/** + * + * @author rtaneja + */ +public class ConvertVarToExplicitTypeTest { + private static final String VAR_CONV_DESC = "Convert var to explicit type";//NOI18N + private static final String VAR_CONV_WARNING = "verifier:" + VAR_CONV_DESC; //NOI18N + + @Test + public void testConvertVartoIntType() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " void m1() {\n" + + " @Deprecated /*some var*/final /*s*/var var = 10;//s\n" + + " }\n" + + "}\n") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .findWarning("3:8-3:56:"+ VAR_CONV_WARNING) + .applyFix() + .assertCompilable() + .assertVerbatimOutput("package test;\n" + + "public class Test {\n" + + " void m1() {\n" + + " @Deprecated /*some var*/final /*s*/int var = 10;//s\n" + + " }\n" + + "}\n"); + } + + @Test + public void testConvertVarToString() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " void m1() {\n" + + " var str = \"Hello\";\n" + + " }\n" + + "}\n") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .findWarning("3:8-3:26:" + VAR_CONV_WARNING) + .applyFix() + .assertCompilable() + .assertVerbatimOutput("package test;\n" + + "public class Test {\n" + + " void m1() {\n" + + " String str = \"Hello\";\n" + + " }\n" + + "}\n"); + } + + @Test + public void testVartoHashMap() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.HashMap;\n" + + "public class Test {\n" + + " {\n" + + " final var map = new HashMap<String, String>();\n" + + " }\n" + + "}\n") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .findWarning("4:8-4:54:" + VAR_CONV_WARNING) + .applyFix() + .assertCompilable() + .assertVerbatimOutput("package test;\n" + + "import java.util.HashMap;\n" + + "public class Test {\n" + + " {\n" + + " final HashMap<String, String> map = new HashMap<String, String>();\n" + + " }\n" + + "}\n"); + } + + @Test + public void testNoVarHintForAnonymousObjType() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " void m1() {\n" + + " var r = new Runnable() {\n" + + " @Override\n" + + " public void run() {\n" + + " }\n" + + " };\n" + + " }\n" + + "}\n") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .assertNotContainsWarnings(VAR_CONV_DESC); + } + + @Test + public void testVarToObjType() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " void m1(){\n" + + " var obj = new Object();\n" + + " }\n" + + "}") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .findWarning("3:8-3:31:" + VAR_CONV_WARNING) + .applyFix() + .assertCompilable() + .assertVerbatimOutput("package test;\n" + + "public class Test {\n" + + " void m1(){\n" + + " Object obj = new Object();\n" + + " }\n" + + "}"); + } + + @Test + public void testVarToArrayType() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " void m1(){\n" + + " var arr = new int[4][];\n" + + " }\n" + + "}") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .findWarning("3:8-3:31:" + VAR_CONV_WARNING) + .applyFix() + .assertCompilable() + .assertVerbatimOutput("package test;\n" + + "public class Test {\n" + + " void m1(){\n" + + " int[][] arr = new int[4][];\n" + + " }\n" + + "}"); + } + + @Test + public void testVarToIntInsideForLoop() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " void m2() {\n" + + " for (var i = 0; i < 10; i++) {\n" + + " i = i + 2;\n" + + " }\n" + + " }\n" + + "}\n") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .findWarning("3:13-3:22:" + VAR_CONV_WARNING) + .applyFix() + .assertCompilable() + .assertVerbatimOutput("package test;\n" + + "public class Test {\n" + + " void m2() {\n" + + " for (int i = 0; i < 10; i++) {\n" + + " i = i + 2;\n" + + " }\n" + + " }\n" + + "}\n"); + } + + @Test + public void testNoHintForExplicitType() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + "void m1(){\n" + + " int k = 20;\n" + + "}\n" + + "}\n") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .assertNotContainsWarnings(VAR_CONV_DESC); + } + + @Test + public void testVarToMethodRetType1() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.ArrayList;\n" + + "public class Test {\n" + + " public void m() {\n" + + " var obj = t();\n" + + " }\n" + + " Object t()\n" + + " {\n" + + " return new ArrayList<String>();\n" + + " }\n" + + "}") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .findWarning("4:8-4:22:" + VAR_CONV_WARNING) + .applyFix() + .assertCompilable() + .assertVerbatimOutput("package test;\n" + + "import java.util.ArrayList;\n" + + "public class Test {\n" + + " public void m() {\n" + + " Object obj = t();\n" + + " }\n" + + " Object t()\n" + + " {\n" + + " return new ArrayList<String>();\n" + + " }\n" + + "}"); + } + + @Test + public void testVarToMethodRetType2() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.Collections;\n" + + "import java.util.List;\n" + + "import java.util.ArrayList;\n" + + "public class Test {\n" + + " public static void main(String[] args) {\n" + + " var list = Collections.unmodifiableList(new ArrayList<String>());\n" + + " }\n" + + "}") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .findWarning("6:8-6:73:" + VAR_CONV_WARNING) + .applyFix() + .assertCompilable() + .assertVerbatimOutput("package test;\n" + + "import java.util.Collections;\n" + + "import java.util.List;\n" + + "import java.util.ArrayList;\n" + + "public class Test {\n" + + " public static void main(String[] args) {\n" + + " List<String> list = Collections.unmodifiableList(new ArrayList<String>());\n" + + " }\n" + + "}"); + } + + @Test + public void testNoVarHintForIntersectionType() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " void m() {\n" + + " var v = get();\n" + + " }\n" + + " <Z extends Runnable & CharSequence> Z get() { return null; }\n" + + "}\n") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .assertNotContainsWarnings(VAR_CONV_DESC); + } + + @Test + public void testVarToGenericWildCardType() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.List;\n" + + "public class Test {\n" + + " void m() {\n" + + " List<? extends String> ll = null;\n" + + " var l = ll.get(0);\n" + + " }\n" + + "}") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .findWarning("5:8-5:26:" + VAR_CONV_WARNING) + .applyFix() + .assertCompilable() + .assertVerbatimOutput("package test;\n" + + "import java.util.List;\n" + + "public class Test {\n" + + " void m() {\n" + + " List<? extends String> ll = null;\n" + + " String l = ll.get(0);\n" + + " }\n" + + "}"); + } + + @Test + public void testVarToGenericWildCardType2() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.List;\n" + + "public class Test {\n" + + " void m() {\n" + + " List<?> ll = null;\n" + + " var l = ll.get(0);\n" + + " }\n" + + "}") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .findWarning("5:8-5:26:" + VAR_CONV_WARNING) + .applyFix() + .assertCompilable() + .assertVerbatimOutput("package test;\n" + + "import java.util.List;\n" + + "public class Test {\n" + + " void m() {\n" + + " List<?> ll = null;\n" + + " Object l = ll.get(0);\n" + + " }\n" + + "}"); + } + + @Test + public void testVarToGenericType2() throws Exception { + HintTest.create() + .input("package test;\n" + + "import java.util.List;\n" + + "public class Test {\n" + + " void m() {\n" + + " var l = listOf(\"\");\n" + + " }\n" + + " <Z> List<Z> listOf(Z z) { return null; }\n" + + "}") + .sourceLevel("1.10") + .run(ConvertVarToExplicitType.class) + .findWarning("4:8-4:27:" + VAR_CONV_WARNING) + .applyFix() + .assertCompilable() + .assertVerbatimOutput("package test;\n" + + "import java.util.List;\n" + + "public class Test {\n" + + " void m() {\n" + + " List<String> l = listOf(\"\");\n" + + " }\n" + + " <Z> List<Z> listOf(Z z) { return null; }\n" + + "}"); + } + +} 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 e3f2356..c9b89b6 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 @@ -1443,7 +1443,12 @@ public class CasualDiff { int addDimensions = 0; if (diffContext.syntheticTrees.contains(oldT.vartype)) { if (!diffContext.syntheticTrees.contains(newT.vartype)) { - copyTo(localPointer, localPointer = oldT.pos); + int varOffset = skipExtraVarKeywordIfPresent(localPointer, oldT.pos); + + if (varOffset == -1) { + copyTo(localPointer, oldT.pos); + } + localPointer = oldT.pos; printer.suppressVariableType = suppressParameterTypes; int l = printer.out.length(); printer.print(newT.vartype); @@ -6027,5 +6032,26 @@ public class CasualDiff { } catch (Exception ex) {} return sb.toString(); } - + + private int skipExtraVarKeywordIfPresent(int start, int end) { + int varoffset = -1; + int newStart = -1; + tokenSequence.move(start); + tokenSequence.moveNext(); + while (tokenSequence.offset() < end) { + JavaTokenId token = tokenSequence.token().id(); + if (token == JavaTokenId.VAR) { + varoffset = tokenSequence.offset(); + copyTo(start, varoffset); + } else if (varoffset > -1) { + if (token != JavaTokenId.WHITESPACE) { + newStart = tokenSequence.offset(); + copyTo(newStart, end); + break; + } + } + tokenSequence.moveNext(); + } + return varoffset; + } } -- To stop receiving notification emails like this one, please contact geert...@apache.org. --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists