This is an automated email from the ASF dual-hosted git repository. akhileshsingh pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push: new e1b1b10 [NETBEANS-5799]: Pattern Matching for Switch hints (preview) (#3156) e1b1b10 is described below commit e1b1b10656a7914f3f5e79ff695756bc03f5bfd6 Author: Sandeep Mishra <72914639+mishrasand...@users.noreply.github.com> AuthorDate: Mon Nov 15 17:10:16 2021 +0530 [NETBEANS-5799]: Pattern Matching for Switch hints (preview) (#3156) * Added hints and test cases for pattern matching for switch --- .../jdk/ConvertToSwitchPatternInstanceOf.java | 178 ++++++++- .../jdk/ConvertToSwitchPatternInstanceOfTest.java | 424 ++++++++++++++++++++- .../netbeans/modules/java/source/TreeShims.java | 11 + .../modules/java/source/builder/TreeFactory.java | 1 + 4 files changed, 585 insertions(+), 29 deletions(-) diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertToSwitchPatternInstanceOf.java b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertToSwitchPatternInstanceOf.java index 85e22bd..3777b88 100644 --- a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertToSwitchPatternInstanceOf.java +++ b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/ConvertToSwitchPatternInstanceOf.java @@ -1,5 +1,3 @@ -package org.netbeans.modules.java.hints.jdk; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,6 +16,8 @@ package org.netbeans.modules.java.hints.jdk; * specific language governing permissions and limitations * under the License. */ +package org.netbeans.modules.java.hints.jdk; + import com.sun.source.tree.BlockTree; import com.sun.source.tree.CaseTree; import com.sun.source.tree.ExpressionStatementTree; @@ -45,6 +45,7 @@ import javax.lang.model.type.TypeMirror; 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.source.TreeShims; import org.netbeans.spi.editor.hints.ErrorDescription; import org.netbeans.spi.editor.hints.Fix; import org.netbeans.spi.java.hints.ErrorDescriptionFactory; @@ -81,14 +82,15 @@ public class ConvertToSwitchPatternInstanceOf { return null; } Tree ifPath = ctx.getPath().getLeaf(); - Name expr0 = ((IdentifierTree) ctx.getVariables().get("$expr0").getLeaf()).getName(); + String expr0 = null; + expr0 = ctx.getVariables().get("$expr0").getLeaf().toString(); int matchVarIndex = 1; while (ifPath != null && ifPath.getKind() == Tree.Kind.IF) { matchVarIndex++; IfTree it = (IfTree) ifPath; if (MatcherUtilities.matches(ctx, new TreePath(ctx.getPath(), it.getCondition()), "($expr" + matchVarIndex + " instanceof $typeI" + matchVarIndex + ")", true) && MatcherUtilities.matches(ctx, new TreePath(ctx.getPath(), it.getThenStatement()), "{ $typeV" + matchVarIndex + " $var" + matchVarIndex + " = ($typeC" + matchVarIndex + ") $expr" + matchVarIndex + "; $other" + matchVarIndex + "$;}", true)) { - if (!((IdentifierTree) ctx.getVariables().get("$expr" + matchVarIndex).getLeaf()).getName().equals(expr0)) { + if (!ctx.getVariables().get("$expr" + matchVarIndex).getLeaf().toString().equals(expr0)) { return null; } for (TreePath tp : ctx.getMultiVariables().get("$other" + matchVarIndex + "$")) { @@ -155,12 +157,105 @@ public class ConvertToSwitchPatternInstanceOf { StatementTree thenBlock = removeFirst ? wc.getTreeMaker().removeBlockStatement((BlockTree) bt, 0) : bt; caseBindPattern.add(wc.getTreeMaker().BindingPattern(wc.getTreeMaker().Variable(wc.getTreeMaker().Modifiers(EnumSet.noneOf(Modifier.class)), var.getName().toString(), iot.getType(), null))); BlockTree blockTree = (BlockTree) thenBlock; + Tree statementTree = blockTree.getStatements().size() == 1 && isValidCaseTree(blockTree.getStatements().get(0))? blockTree.getStatements().get(0) : thenBlock; + CaseTree caseMultipleSwitchPatterns = wc.getTreeMaker().CasePatterns(caseBindPattern, statementTree); + ctl.add(caseMultipleSwitchPatterns); + } + List<Tree> caseDefaultLabel = new LinkedList<>(); + caseDefaultLabel.add(wc.getTreeMaker().Identifier("default")); + BlockTree elseTree = (BlockTree) ifPath; + if (elseTree == null) { + elseTree = wc.getTreeMaker().Block(new ArrayList<>(), false); + } + + Tree defaultTree = elseTree.getStatements().size() == 1 && isValidCaseTree(elseTree.getStatements().get(0))? elseTree.getStatements().get(0) : elseTree; + CaseTree caseMultipleSwitchPatterns = wc.getTreeMaker().CasePatterns(caseDefaultLabel, defaultTree); + ctl.add(caseMultipleSwitchPatterns); + wc.rewrite((IfTree) main.getLeaf(), wc.getTreeMaker().Switch(iot.getExpression(), ctl)); + } + + } + + @TriggerPatterns({ + @TriggerPattern(value = "if ($expr0 instanceof $typeI0 $var0) { $statements0$;} else if ($expr1 instanceof $typeI1 $var1) { $statements1$;} else $else$;") + }) + public static ErrorDescription patternMatchToSwitch(HintContext ctx) { + TreePath parent = ctx.getPath().getParentPath(); + if (parent.getLeaf().getKind() == Tree.Kind.IF) { + return null; + } + Tree ifPath = ctx.getPath().getLeaf(); + String expr0 = null; + expr0 = ctx.getVariables().get("$expr0").getLeaf().toString(); + int matchVarIndex = 1; + while (ifPath != null && ifPath.getKind() == Tree.Kind.IF) { + matchVarIndex++; + IfTree it = (IfTree) ifPath; + if (MatcherUtilities.matches(ctx, new TreePath(ctx.getPath(), it.getCondition()), "($expr" + matchVarIndex + " instanceof $typeI" + matchVarIndex + " $var" + matchVarIndex + ")", true) + && MatcherUtilities.matches(ctx, new TreePath(ctx.getPath(), it.getThenStatement()), "{ $other" + matchVarIndex + "$;}", true)) { + if (ctx.getMultiVariables().get("$other" + matchVarIndex + "$").isEmpty()) { + return null; + } + if (!(ctx.getVariables().get("$expr" + matchVarIndex).getLeaf().toString().equals(expr0))) { + return null; + } + for (TreePath tp : ctx.getMultiVariables().get("$other" + matchVarIndex + "$")) { + if (tp.getLeaf().getKind() == Tree.Kind.BREAK || tp.getLeaf().getKind() == Tree.Kind.CONTINUE) { + return null; + } + } + } else { + return null; + } + ifPath = it.getElseStatement(); + } + + Fix fix = new FixPatternMatchToSwitch(ctx.getInfo(), ctx.getPath(), false, Collections.emptySet()).toEditorFix(); + return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), Bundle.ERR_ConvertToSwitchPatternInstanceOf(), fix); + + } + + private static final class FixPatternMatchToSwitch extends JavaFix { + private final boolean removeFirst; + + public FixPatternMatchToSwitch(CompilationInfo info, TreePath main, boolean removeFirst, Set<TreePath> replaceOccurrences) { + super(info, main); + this.removeFirst = removeFirst; + } + + @Override + protected String getText() { + return Bundle.FIX_ConvertToSwitchPatternInstanceOf(); + } - Tree defaultTree = null; - defaultTree = blockTree.getStatements().size() == 1 && isValidCaseTree(blockTree.getStatements().get(0))? blockTree.getStatements().get(0) : thenBlock; + @Override + protected void performRewrite(JavaFix.TransformationContext ctx) { + WorkingCopy wc = ctx.getWorkingCopy(); + TreePath main = ctx.getPath(); + List<CaseTree> ctl = new LinkedList<>(); + InstanceOfTree iot = null; - CaseTree casePatterns = wc.getTreeMaker().CasePatterns(caseBindPattern, defaultTree); - ctl.add(casePatterns); + Tree ifPath = ctx.getPath().getLeaf(); + int matchVarIndex = 1; + List<IfTree> ifTrees = new ArrayList<>(); + while (ifPath != null && ifPath.getKind() == Tree.Kind.IF) { + matchVarIndex++; + IfTree it = (IfTree) ifPath; + ifTrees.add(it); + ifPath = it.getElseStatement(); + } + + for (IfTree ifTree : ifTrees) { + List<Tree> caseBindPattern = new LinkedList<>(); + iot = (InstanceOfTree) ((ParenthesizedTree) ifTree.getCondition()).getExpression(); + StatementTree bt = ifTree.getThenStatement(); + StatementTree thenBlock = removeFirst ? wc.getTreeMaker().removeBlockStatement((BlockTree) bt, 0) : bt; + Tree pattern = TreeShims.getPattern(iot); + caseBindPattern.add(pattern); + BlockTree blockTree = (BlockTree) thenBlock; + Tree statementTree = blockTree.getStatements().size() == 1 && isValidCaseTree(blockTree.getStatements().get(0))? blockTree.getStatements().get(0) : thenBlock; + CaseTree caseMultipleSwitchPatterns = wc.getTreeMaker().CasePatterns(caseBindPattern, statementTree); + ctl.add(caseMultipleSwitchPatterns); } List<Tree> caseDefaultLabel = new LinkedList<>(); caseDefaultLabel.add(wc.getTreeMaker().Identifier("default")); @@ -177,11 +272,72 @@ public class ConvertToSwitchPatternInstanceOf { } + + @TriggerTreeKind(Tree.Kind.SWITCH) + public static ErrorDescription switchPatternMatchToSwitchNull(HintContext ctx) { + + SwitchTree switchTree = (SwitchTree) ctx.getPath().getLeaf(); + boolean isPatternMatch = false; + isPatternMatch = TreeShims.isPatternMatch(switchTree); + if (!isPatternMatch) { + return null; + } + Tree expression = ((ParenthesizedTree) switchTree.getExpression()).getExpression(); + Tree parent = (Tree) ctx.getPath().getParentPath().getLeaf(); + int indexOf; + if (parent instanceof BlockTree) { + indexOf = ((BlockTree) parent).getStatements().indexOf(switchTree) - 1; + } else { + return null; + } + Tree ifTree = ((BlockTree) parent).getStatements().get(indexOf); + if ((!(ifTree instanceof IfTree) || !MatcherUtilities.matches(ctx, new TreePath(ctx.getPath(), ((IfTree) ifTree).getCondition()), "($expr0 == null)", true)) + || !(ctx.getVariables().get("$expr0").getLeaf().toString().equals(expression.toString()))) { + return null; + } + Fix fix = new FixSwitchPatternMatchToSwitchNull(ctx.getInfo(), ctx.getPath().getParentPath(), indexOf).toEditorFix(); + return ErrorDescriptionFactory.forTree(ctx, ifTree, Bundle.ERR_ConvertToSwitchPatternInstanceOf(), fix); + + } + + private static final class FixSwitchPatternMatchToSwitchNull extends JavaFix { + + private final int indexOf; + + public FixSwitchPatternMatchToSwitchNull(CompilationInfo info, TreePath path, int indexOf) { + super(info, path); + this.indexOf = indexOf; + } + + @Override + protected String getText() { + return Bundle.FIX_ConvertToSwitchPatternInstanceOf(); + } + + @Override + protected void performRewrite(TransformationContext ctx) throws Exception { + WorkingCopy wc = ctx.getWorkingCopy(); + TreePath main = ctx.getPath(); + TreeMaker make = wc.getTreeMaker(); + List<Tree> caseNullLabel = new LinkedList<>(); + SwitchTree switchTree = (SwitchTree) ((BlockTree) main.getLeaf()).getStatements().get(indexOf + 1); + + Tree ifTree = ((BlockTree) main.getLeaf()).getStatements().get(indexOf); + StatementTree thenStatement = ((IfTree) ifTree).getThenStatement(); + caseNullLabel.add(wc.getTreeMaker().Identifier("null")); + BlockTree blockTree = (BlockTree)thenStatement; + Tree statementTree = blockTree.getStatements().size() == 1 && isValidCaseTree(blockTree.getStatements().get(0))? blockTree.getStatements().get(0) : blockTree; + CaseTree caseMultipleSwitchPatterns = wc.getTreeMaker().CasePatterns(caseNullLabel, statementTree); + SwitchTree insertSwitchCase = make.insertSwitchCase(switchTree, 0, caseMultipleSwitchPatterns); + wc.rewrite(switchTree, insertSwitchCase); + BlockTree removeBlockStatement = make.removeBlockStatement((BlockTree) main.getLeaf(), indexOf); + wc.rewrite(main.getLeaf(), removeBlockStatement); + } + } + private static boolean isValidCaseTree(Tree tree){ return ((tree instanceof BlockTree) || (tree instanceof ExpressionStatementTree) - || (tree instanceof ThrowTree) - || (tree instanceof CaseTree)); + || (tree instanceof ThrowTree)); } } - diff --git a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToSwitchPatternInstanceOfTest.java b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToSwitchPatternInstanceOfTest.java index 30b6eab..3659b3c 100644 --- a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToSwitchPatternInstanceOfTest.java +++ b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/ConvertToSwitchPatternInstanceOfTest.java @@ -21,6 +21,7 @@ package org.netbeans.modules.java.hints.jdk; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.java.hints.test.api.HintTest; import javax.lang.model.SourceVersion; +import org.testng.annotations.Test; /** * @@ -32,6 +33,7 @@ public class ConvertToSwitchPatternInstanceOfTest extends NbTestCase { super(name); } + @Test public void testSimple() throws Exception { try { SourceVersion.valueOf("RELEASE_17"); //NOI18N @@ -42,37 +44,423 @@ public class ConvertToSwitchPatternInstanceOfTest extends NbTestCase { HintTest.create() .input("package test;\n" + "public class Test {\n" - + " private void test(Object o) {\n" - + " if (o instanceof String) {\n" - + " String s = (String) o;\n" - + " System.out.println(s + \" String\");\n" - + " } else if (o instanceof StringBuilder) {\n" - + " StringBuilder sb = (StringBuilder) o;\n" - + " System.out.println(sb + \" StringBuilder\");\n" - + " } else if (o instanceof CharSequence) {\n" - + " CharSequence cs = (CharSequence) o;\n" - + " System.out.println(cs + \" CharSequence\");\n" - + " } else {\n" - + " System.out.println(\"else\");\n" + + " private int test(Object o){\n" + + " if(o instanceof String){\n" + + " String s = (String)o;\n" + + " System.out.println(s);\n" + + " }else if(o instanceof Integer){\n" + + " Integer i = (Integer)o;\n" + + " System.out.println(i);\n" + + " }else{\n" + + " System.out.println(\"else\");\n" + + " }\n" + + " return -1;\n" + + " }\n" + + "}\n" + ) + .sourceLevel("17") + .options("--enable-preview") + .run(ConvertToSwitchPatternInstanceOf.class) + .findWarning("3:8-3:10:verifier:" + Bundle.ERR_ConvertToSwitchPatternInstanceOf()) + .applyFix() + .assertCompilable() + .assertOutput("package test;\n" + + "public class Test {\n" + + " private int test(Object o){\n" + + " switch (o) {\n" + + " case String s -> System.out.println(s);\n" + + " case Integer i -> System.out.println(i);\n" + + " case default -> System.out.println(\"else\");\n" + " }\n" + + " return -1;\n" + " }\n" + + "}\n"); + } + + @Test + public void testSimpleNoHint() throws Exception { + try { + SourceVersion.valueOf("RELEASE_17"); //NOI18N + } catch (IllegalArgumentException ex) { + //OK, no RELEASE_17, skip tests + return; + } + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " private int test(Object o, Object p){\n" + + " if(o instanceof String){\n" + + " String s = (String)o;\n" + + " System.out.println(s);\n" + + " }else if(p instanceof Integer){\n" + + " Integer i = (Integer)p;\n" + + " System.out.println(i);\n" + + " }else{\n" + + " System.out.println(\"else\");\n" + + " }\n" + + " return -1;\n" + + " }\n" + + "}\n" + ) + .sourceLevel("17") + .options("--enable-preview") + .run(ConvertToSwitchPatternInstanceOf.class) + .assertNotContainsWarnings("3:8-3:10:verifier:" + Bundle.ERR_ConvertToSwitchPatternInstanceOf()); + } + + @Test + public void testSimplePatternMatch() throws Exception { + try { + SourceVersion.valueOf("RELEASE_17"); + } catch (IllegalArgumentException ex) { + //OK, skip test + return; + } + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " static String formatter(Object o) {\n" + + " String formatted = \"unknown\";\n" + + " if (o instanceof Integer i) {\n" + + " formatted = String.format(\"int %d\", i);\n" + + " } else if (o instanceof Long l) {\n" + + " formatted = String.format(\"long %d\", l);\n" + + " } else if (o instanceof Double d) {\n" + + " formatted = String.format(\"double %f\", d);\n" + + " } else if (o instanceof String s) {\n" + + " formatted = String.format(\"String %s\", s);\n" + + " }\n" + + " return formatted;\n" + + " }" + + "}\n") + .sourceLevel(SourceVersion.latest().name()) + .options("--enable-preview") + .run(ConvertToSwitchPatternInstanceOf.class) + .findWarning("4:8-4:10:verifier:" + Bundle.ERR_ConvertToSwitchPatternInstanceOf()) + .applyFix() + .assertCompilable() + .assertOutput("package test;\n" + + "public class Test {\n" + + " static String formatter(Object o) {\n" + + " String formatted = \"unknown\";\n" + + " switch (o) {\n" + + " case Integer i -> formatted = String.format(\"int %d\", i);\n" + + " case Long l -> formatted = String.format(\"long %d\", l);\n" + + " case Double d -> formatted = String.format(\"double %f\", d);\n" + + " case String s -> formatted = String.format(\"String %s\", s);\n" + + " case default -> {\n" + + " }\n" + + " }\n" + + " return formatted;\n" + + " }\n" + + "}\n"); + } + + @Test + public void testSimplePatternMatchNoHint() throws Exception { + try { + SourceVersion.valueOf("RELEASE_17"); + } catch (IllegalArgumentException ex) { + //OK, skip test + return; + } + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " static String formatter(Object o, Object p) {\n" + + " String formatted = \"unknown\";\n" + + " if (o instanceof Integer i) {\n" + + " formatted = String.format(\"int %d\", i);\n" + + " } else if (o instanceof Long l) {\n" + + " formatted = String.format(\"long %d\", l);\n" + + " } else if (p instanceof Double d) {\n" + + " formatted = String.format(\"double %f\", p);\n" + + " } else if (o instanceof String s) {\n" + + " formatted = String.format(\"String %s\", s);\n" + + " }\n" + + " return formatted;\n" + + " }" + + "}\n") + .sourceLevel(SourceVersion.latest().name()) + .options("--enable-preview") + .run(ConvertToSwitchPatternInstanceOf.class) + .assertNotContainsWarnings("4:8-4:10:verifier:" + Bundle.ERR_ConvertToSwitchPatternInstanceOf()); + } + + @Test + public void testSimpleSwitchWithNull() throws Exception { + try { + SourceVersion.valueOf("RELEASE_17"); + } catch (IllegalArgumentException ex) { + //OK, skip test + return; + } + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " private String formatter(Object o) {\n" + + " String formatted = \"unknown\";\n" + + " if (o == null) {\n" + + " formatted = \"null\";\n" + + " }\n" + + " switch (o) {\n" + + " case Integer i ->\n" + + " formatted = String.format(\"int %d\", i);\n" + + " case Long l ->\n" + + " formatted = String.format(\"long %d\", l);\n" + + " case Double d ->\n" + + " formatted = String.format(\"double %f\", d);\n" + + " case String s ->\n" + + " formatted = String.format(\"String %s\", s);\n" + + " case default -> formatted = \"unknown\";\n" + + " }\n" + + " return formatted;\n" + + " }" + + "}\n") + .sourceLevel(SourceVersion.latest().name()) + .options("--enable-preview") + .run(ConvertToSwitchPatternInstanceOf.class) + .findWarning("4:8-4:24:verifier:" + Bundle.ERR_ConvertToSwitchPatternInstanceOf()) + .applyFix() + .assertCompilable() + .assertOutput("package test;\n" + + "public class Test {\n" + + " private String formatter(Object o) {\n" + + " String formatted = \"unknown\";\n" + + " switch (o) {\n" + + " case null -> formatted = \"null\";\n" + + " case Integer i ->\n" + + " formatted = String.format(\"int %d\", i);\n" + + " case Long l ->\n" + + " formatted = String.format(\"long %d\", l);\n" + + " case Double d ->\n" + + " formatted = String.format(\"double %f\", d);\n" + + " case String s ->\n" + + " formatted = String.format(\"String %s\", s);\n" + + " case default -> \n" + + " formatted = \"unknown\";\n" + + " }\n" + + " return formatted;\n" + + " }\n" + + "}\n"); + } + + @Test + public void testSimpleSwitchWithNullNoHint() throws Exception { + try { + SourceVersion.valueOf("RELEASE_17"); + } catch (IllegalArgumentException ex) { + //OK, skip test + return; + } + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " private String formatter(Object o, Object p) {\n" + + " String formatted = \"unknown\";\n" + + " if (o == null) {\n" + + " formatted = \"null\";\n" + + " }\n" + + " switch (p) {\n" + + " case Integer i ->\n" + + " formatted = String.format(\"int %d\", i);\n" + + " case Long l ->\n" + + " formatted = String.format(\"long %d\", l);\n" + + " case Double d ->\n" + + " formatted = String.format(\"double %f\", d);\n" + + " case String s ->\n" + + " formatted = String.format(\"String %s\", s);\n" + + " case default -> formatted = \"unknown\";\n" + + " }\n" + + " return formatted;\n" + + " }" + + "}\n") + .sourceLevel(SourceVersion.latest().name()) + .options("--enable-preview") + .run(ConvertToSwitchPatternInstanceOf.class) + .assertNotContainsWarnings("4:8-4:24:verifier:" + Bundle.ERR_ConvertToSwitchPatternInstanceOf()); + } + + @Test + public void testSingleStatementsStaticVariable() throws Exception { + try { + SourceVersion.valueOf("RELEASE_17"); + } catch (IllegalArgumentException ex) { + //OK, skip test + return; + } + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " private int formatter(Object o) {\n" + + " String formatted = \"unknown\";\n" + + " if (Test2.a instanceof String) { \n" + + " String s = (String) Test2.a;\n" + + " System.out.println(s);\n" + + " } else if (Test2.a instanceof Integer) {\n" + + " Integer i = (Integer) Test2.a;\n" + + " System.out.println(i);\n" + + " } else if (Test2.a instanceof Character) {\n" + + " Character c = (Character) Test2.a;\n" + + " return 1;\n" + + " } else {\n" + + " return 1;\n" + + " }\n" + + " return -1;\n" + + " }" + + "}\n" + + "class Test2{\n" + + " public static Object a;\n" + "}") .sourceLevel(SourceVersion.latest().name()) .options("--enable-preview") .run(ConvertToSwitchPatternInstanceOf.class) - .findWarning("3:8-3:10:verifier:" + Bundle.ERR_ConvertToSwitchPatternInstanceOf()) + .findWarning("4:8-4:10:verifier:" + Bundle.ERR_ConvertToSwitchPatternInstanceOf()) + .applyFix() + .assertCompilable() + .assertOutput("package test;\n" + + "public class Test {\n" + + " private int formatter(Object o) {\n" + + " String formatted = \"unknown\";\n" + + " switch (Test2.a) {\n" + + " case String s -> System.out.println(s);\n" + + " case Integer i -> System.out.println(i);\n" + + " case Character c -> {\n" + + " return 1;\n" + + " }\n" + + " case default -> {\n" + + " return 1;\n" + + " }\n" + + " }\n" + + " return -1;\n" + + " }\n" + + "}\n" + + "class Test2{\n" + + " public static Object a;\n" + + "}"); + } + + @Test + public void testMultipleStatements() throws Exception { + try { + SourceVersion.valueOf("RELEASE_17"); + } catch (IllegalArgumentException ex) { + //OK, skip test + return; + } + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " private int formatter(Object o) {\n" + + " String formatted = \"unknown\";\n" + + " if (o instanceof String) { \n" + + " String s = (String) o;\n" + + " formatted = \"String\";\n" + + " System.out.println(s);\n" + + " } else if (o instanceof Integer) {\n" + + " Integer i = (Integer) o;\n" + + " formatted = \"Integer\";\n" + + " System.out.println(i);\n" + + " } else if (o instanceof Character) {\n" + + " Character c = (Character) o;\n" + + " formatted = \"Character\";\n" + + " return 1;\n" + + " } else {\n" + + " formatted = \"else\";\n" + + " return 1;\n" + + " }\n" + + " return -1;\n" + + " }" + + "}\n") + .sourceLevel(SourceVersion.latest().name()) + .options("--enable-preview") + .run(ConvertToSwitchPatternInstanceOf.class) + .findWarning("4:8-4:10:verifier:" + Bundle.ERR_ConvertToSwitchPatternInstanceOf()) .applyFix() .assertCompilable() .assertOutput("package test;\n" + "public class Test {\n" - + " private void test(Object o) {\n" + + " private int formatter(Object o) {\n" + + " String formatted = \"unknown\";\n" + " switch (o) {\n" - + " case String s -> System.out.println(s + \" String\");\n" - + " case StringBuilder sb -> System.out.println(sb + \" StringBuilder\");\n" - + " case CharSequence cs -> System.out.println(cs + \" CharSequence\");\n" - + " case default -> System.out.println(\"else\");\n" + + " case String s -> {\n" + + " formatted = \"String\";\n" + + " System.out.println(s);\n" + + " }\n" + + " case Integer i -> {\n" + + " formatted = \"Integer\";\n" + + " System.out.println(i);\n" + + " }\n" + + " case Character c -> {\n" + + " formatted = \"Character\";\n" + + " return 1;\n" + + " }\n" + + " case default -> {\n" + + " formatted = \"else\";\n" + + " return 1; \n" + + " }\n" + " }\n" + + " return -1;\n" + " }\n" + "}\n"); } + + @Test + public void testEmptyStatementsMethodInvocation() throws Exception { + try { + SourceVersion.valueOf("RELEASE_17"); + } catch (IllegalArgumentException ex) { + //OK, skip test + return; + } + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " private int formatter(Object o) {\n" + + " String formatted = \"unknown\";\n" + + " if (Test2.m() instanceof String) { \n" + + " String s = (String) Test2.m();\n" + + " System.out.println(s);\n" + + " } else if (Test2.m() instanceof Integer) {\n" + + " Integer i = (Integer) Test2.m();\n" + + " System.out.println(i);\n" + + " } else if (Test2.m() instanceof Character) {\n" + + " Character c = (Character) Test2.m();\n" + + " } else {\n" + + " }\n" + + " return -1;\n" + + " }" + + "}\n" + + "class Test2{\n" + + " public static Object m(){\n" + + " return \"method invocation\";\n" + + " }" + + "}") + .sourceLevel(SourceVersion.latest().name()) + .options("--enable-preview") + .run(ConvertToSwitchPatternInstanceOf.class) + .findWarning("4:8-4:10:verifier:" + Bundle.ERR_ConvertToSwitchPatternInstanceOf()) + .applyFix() + .assertCompilable() + .assertOutput("package test;\n" + + "public class Test {\n" + + " private int formatter(Object o) {\n" + + " String formatted = \"unknown\";\n" + + " switch (Test2.m()) {\n" + + " case String s -> System.out.println(s);\n" + + " case Integer i -> System.out.println(i);\n" + + " case Character c -> {\n" + + " }\n" + + " case default -> {\n" + + " }\n" + + " }\n" + + " return -1;\n" + + " }\n" + + "}\n" + + "class Test2{\n" + + " public static Object m(){\n" + + " return \"method invocation\";\n" + + " }" + + "}"); + } } diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java b/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java index fb9b24c..a27181e 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/TreeShims.java @@ -372,6 +372,17 @@ public class TreeShims { } } + public static boolean isPatternMatch(Tree node) { + if (isJDKVersionRelease17_Or_Above()) { + try { + return node.getClass().getField("patternSwitch").getBoolean(node); + } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException ex) { + throw TreeShims.<RuntimeException>throwAny(ex); + } + } + return false; + } + public static boolean isJDKVersionSupportEnablePreview() { return Integer.valueOf(SourceVersion.latest().name().split("_")[1]).compareTo(PATTERN_MATCHING_INSTANCEOF_PREVIEW_JDK_VERSION) <= 0; } diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java b/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java index 94cd00c..0601b06 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/builder/TreeFactory.java @@ -280,6 +280,7 @@ public class TreeFactory { } } + public CaseTree CaseMultiplePatterns(List<? extends Tree> expressions, List<? extends StatementTree> statements) { ListBuffer<JCStatement> lb = new ListBuffer<JCStatement>(); for (StatementTree t : statements) --------------------------------------------------------------------- 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