You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by ak...@apache.org on 2021/11/15 11:40:26 UTC

[netbeans] branch master updated: [NETBEANS-5799]: Pattern Matching for Switch hints (preview) (#3156)

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 <72...@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-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists