diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java index 535041e67fb..a9cd89c4ab9 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java @@ -21,10 +21,8 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.function.IntPredicate; @@ -1431,7 +1429,6 @@ private boolean checkAndFlagDefaultRecord(BlockScope skope, CompilerOptions comp return false; } private boolean isExhaustiveWithCaseTypes(List allAllowedTypes, List listedTypes) { - // first KISS (Keep It Simple Stupid) int pendingTypes = allAllowedTypes.size(); for (ReferenceBinding pt : allAllowedTypes) { /* Per JLS 14.11.1.1: A type T that names an abstract sealed class or sealed interface is covered @@ -1450,63 +1447,7 @@ private boolean isExhaustiveWithCaseTypes(List allAllowedTypes } } } - if (pendingTypes == 0) - return true; - // else - #KICKME (Keep It Complicated Keep Me Employed)" - List coveredTypes = new ArrayList<>(listedTypes); - List remainingTypes = new ArrayList<>(allAllowedTypes); - remainingTypes.removeAll(coveredTypes); - - Map> impliedTypes = new HashMap<>(); - - for (ReferenceBinding type : remainingTypes) { - impliedTypes.put(type, new ArrayList<>()); - List typesToAdd = new ArrayList<>(); - for (ReferenceBinding impliedType : allAllowedTypes) { - if (impliedType.equals(type)) continue; - if (type.isClass()) { - if (impliedType.isAbstract() && type.superclass().equals(impliedType)) { - typesToAdd.add(impliedType); - } - if (Arrays.asList(type.superInterfaces()).contains(impliedType)) - typesToAdd.add(impliedType); - } else if (type.isInterface()) { - if (Arrays.asList(impliedType.superInterfaces()).contains(type)) - typesToAdd.add(impliedType); - } - } - if (!typesToAdd.isEmpty()) { - impliedTypes.get(type).addAll(typesToAdd); - } - } - boolean delta = true; - while (delta) { - delta = false; - List typesToAdd = new ArrayList<>(); - for (ReferenceBinding type : remainingTypes) { - boolean add = false; - if (type.isClass()) { - for (TypeBinding tb : impliedTypes.get(type)) { - if (coveredTypes.contains(tb)) { - add = true; - break; - } - } - } else if (type.isInterface()) { - add = coveredTypes.containsAll(impliedTypes.get(type)); - } - if (add) { - typesToAdd.add(type); - } - } - if (!typesToAdd.isEmpty()) { - remainingTypes.removeAll(typesToAdd); - coveredTypes.addAll(typesToAdd); - typesToAdd.clear(); - delta = true; - } - } - return remainingTypes.isEmpty(); + return pendingTypes == 0; } private boolean needPatternDispatchCopy() { if (this.containsPatterns || (this.switchBits & QualifiedEnum) != 0) diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_20.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_20.java deleted file mode 100644 index 36988fa990e..00000000000 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_20.java +++ /dev/null @@ -1,183 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2023 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.jdt.core.tests.compiler.regression; - -import java.io.File; - -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.tests.util.Util; - -import junit.framework.Test; - -public class BatchCompilerTest_20 extends AbstractBatchCompilerTest { - - static { -// TESTS_NAMES = new String[] { "testIssue558_1" }; - // TESTS_NUMBERS = new int[] { 306 }; - // TESTS_RANGE = new int[] { 298, -1 }; - } - /** - * This test suite only needs to be run on one compliance. - * - * @see TestAll - */ - public static Test suite() { - return buildMinimalComplianceTestSuite(testClass(), F_20); - } - - public static Class testClass() { - return BatchCompilerTest_20.class; - } - - public BatchCompilerTest_20(String name) { - super(name); - } - - public void testIssue558_1() throws Exception { - String path = LIB_DIR; - String libPath = null; - if (path.endsWith(File.separator)) { - libPath = path + "lib.jar"; - } else { - libPath = path + File.separator + "lib.jar"; - } - Util.createJar(new String[] { - "p/Color.java", - "package p;\n" + - "public enum Color {\n" + - " R, Y;\n" + - " public static Color getColor() {\n" + - " return R;\n" + - " }\n" + - "}", - }, - libPath, - JavaCore.VERSION_20); - this.runConformTest( - new String[] { - "src/p/X.java", - "package p;\n" - + "import p.Color;\n" - + "public class X {\n" - + " public static void main(String argv[]) {\n" - + " Color c = Color.getColor();\n" - + " try {\n" - + " int a = switch (c) {\n" - + " case R -> 1;\n" - + " case Y -> 2;\n" - + " };\n" - + " } catch (MatchException e) {\n" - + " System.out.print(\"OK\");\n" - + " } catch (Exception e) {\n" - + " System.out.print(\"NOT OK: \" + e);\n" - + " }\n" - + " System.out.print(\"END\");\n" - + " }\n" - + "}", - }, - "\"" + OUTPUT_DIR + File.separator + "src/p/X.java\"" - + " -cp \"" + LIB_DIR + File.separator + "lib.jar\"" - + " -sourcepath \"" + OUTPUT_DIR + File.separator + "src\"" - + " --enable-preview -source 20 -warn:none" - + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ", - "", - "", - true); - this.verifier.execute("p.X", new String[] {OUTPUT_DIR + File.separator + "bin", libPath}, new String[0], new String[] {"--enable-preview"}); - assertEquals("Incorrect output", "END", this.verifier.getExecutionOutput()); - Util.createJar(new String[] { - "p/Color.java", - "package p;\n" + - "public enum Color {\n" + - " R, Y, B;\n" + - " public static Color getColor() {\n" + - " return B;\n" + - " }\n" + - "}", - }, - libPath, - JavaCore.VERSION_20); - this.verifier.execute("p.X", new String[] {OUTPUT_DIR + File.separator + "bin", libPath}, new String[0], new String[] {"--enable-preview"}); - assertEquals("Incorrect output", "OKEND", this.verifier.getExecutionOutput()); - } - public void testIssue558_2() throws Exception { - String path = LIB_DIR; - String libPath = null; - if (path.endsWith(File.separator)) { - libPath = path + "lib.jar"; - } else { - libPath = path + File.separator + "lib.jar"; - } - Util.createJar(new String[] { - "p/I.java", - "package p;\n" + - "public sealed interface I {\n" + - " public static I getImpl() {\n" + - " return new A();\n" + - " }\n" + - "}\n" + - "final class A implements I {}\n" + - "final class B implements I {}", - }, - libPath, - JavaCore.VERSION_20); - this.runConformTest( - new String[] { - "src/p/X.java", - "package p;\n" - + "import p.I;\n" - + "public class X {\n" - + " public static void main(String argv[]) {\n" - + " I i = I.getImpl();\n" - + " try {\n" - + " int r = switch (i) {\n" - + " case A a -> 1;\n" - + " case B b -> 2;\n" - + " };\n" - + " } catch (MatchException e) {\n" - + " System.out.print(\"OK\");\n" - + " } catch (Exception e) {\n" - + " System.out.print(\"NOT OK: \" + e);\n" - + " }\n" - + " System.out.print(\"END\");\n" - + " }\n" - + "}", - }, - "\"" + OUTPUT_DIR + File.separator + "src/p/X.java\"" - + " -cp \"" + LIB_DIR + File.separator + "lib.jar\"" - + " -sourcepath \"" + OUTPUT_DIR + File.separator + "src\"" - + " --enable-preview -source 20 -warn:none" - + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ", - "", - "", - true); - this.verifier.execute("p.X", new String[] {OUTPUT_DIR + File.separator + "bin", libPath}, new String[0], new String[] {"--enable-preview"}); - assertEquals("Incorrect output", "END", this.verifier.getExecutionOutput()); - Util.createJar(new String[] { - "p/I.java", - "package p;\n" + - "public sealed interface I {\n" + - " public static I getImpl() {\n" + - " return new C();\n" + - " }\n" + - "}\n" + - "final class A implements I {}\n" + - "final class B implements I {}\n" + - "final class C implements I {}", - }, - libPath, - JavaCore.VERSION_20); - this.verifier.execute("p.X", new String[] {OUTPUT_DIR + File.separator + "bin", libPath}, new String[0], new String[] {"--enable-preview"}); - assertEquals("Incorrect output", "OKEND", this.verifier.getExecutionOutput()); - } -} diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_21.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_21.java index 9d5eb3c101f..23180d0b346 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_21.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_21.java @@ -182,4 +182,143 @@ public void testGHI1774_Statement() throws Exception { assertEquals("Incorrect output", "OK!END", this.verifier.getExecutionOutput()); } + public void testIssue558_1() throws Exception { + String path = LIB_DIR; + String libPath = null; + if (path.endsWith(File.separator)) { + libPath = path + "lib.jar"; + } else { + libPath = path + File.separator + "lib.jar"; + } + Util.createJar(new String[] { + "p/Color.java", + "package p;\n" + + "public enum Color {\n" + + " R, Y;\n" + + " public static Color getColor() {\n" + + " return R;\n" + + " }\n" + + "}", + }, + libPath, + JavaCore.VERSION_21); + this.runConformTest( + new String[] { + "src/p/X.java", + "package p;\n" + + "import p.Color;\n" + + "public class X {\n" + + " public static void main(String argv[]) {\n" + + " Color c = Color.getColor();\n" + + " try {\n" + + " int a = switch (c) {\n" + + " case R -> 1;\n" + + " case Y -> 2;\n" + + " };\n" + + " } catch (MatchException e) {\n" + + " System.out.print(\"OK\");\n" + + " } catch (Exception e) {\n" + + " System.out.print(\"NOT OK: \" + e);\n" + + " }\n" + + " System.out.print(\"END\");\n" + + " }\n" + + "}", + }, + "\"" + OUTPUT_DIR + File.separator + "src/p/X.java\"" + + " -cp \"" + LIB_DIR + File.separator + "lib.jar\"" + + " -sourcepath \"" + OUTPUT_DIR + File.separator + "src\"" + + " -source 21 -warn:none" + + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ", + "", + "", + true); + this.verifier.execute("p.X", new String[] {OUTPUT_DIR + File.separator + "bin", libPath}, new String[0], null); + assertEquals("Incorrect output", "END", this.verifier.getExecutionOutput()); + Util.createJar(new String[] { + "p/Color.java", + "package p;\n" + + "public enum Color {\n" + + " R, Y, B;\n" + + " public static Color getColor() {\n" + + " return B;\n" + + " }\n" + + "}", + }, + libPath, + JavaCore.VERSION_21); + this.verifier.execute("p.X", new String[] {OUTPUT_DIR + File.separator + "bin", libPath}, new String[0], null); + assertEquals("Incorrect output", "OKEND", this.verifier.getExecutionOutput()); + } + + public void testIssue558_2() throws Exception { + String path = LIB_DIR; + String libPath = null; + if (path.endsWith(File.separator)) { + libPath = path + "lib.jar"; + } else { + libPath = path + File.separator + "lib.jar"; + } + Util.createJar(new String[] { + "p/I.java", + "package p;\n" + + "public sealed interface I {\n" + + " public static I getImpl() {\n" + + " return new A();\n" + + " }\n" + + "}\n" + + "final class A implements I {}\n" + + "final class B implements I {}", + }, + libPath, + JavaCore.VERSION_21); + this.runConformTest( + new String[] { + "src/p/X.java", + "package p;\n" + + "import p.I;\n" + + "public class X {\n" + + " public static void main(String argv[]) {\n" + + " I i = I.getImpl();\n" + + " try {\n" + + " int r = switch (i) {\n" + + " case A a -> 1;\n" + + " case B b -> 2;\n" + + " };\n" + + " } catch (MatchException e) {\n" + + " System.out.print(\"OK\");\n" + + " } catch (Exception e) {\n" + + " System.out.print(\"NOT OK: \" + e);\n" + + " }\n" + + " System.out.print(\"END\");\n" + + " }\n" + + "}", + }, + "\"" + OUTPUT_DIR + File.separator + "src/p/X.java\"" + + " -cp \"" + LIB_DIR + File.separator + "lib.jar\"" + + " -sourcepath \"" + OUTPUT_DIR + File.separator + "src\"" + + " -source 21 -warn:none" + + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ", + "", + "", + true); + this.verifier.execute("p.X", new String[] {OUTPUT_DIR + File.separator + "bin", libPath}, new String[0], null); + assertEquals("Incorrect output", "END", this.verifier.getExecutionOutput()); + Util.createJar(new String[] { + "p/I.java", + "package p;\n" + + "public sealed interface I {\n" + + " public static I getImpl() {\n" + + " return new C();\n" + + " }\n" + + "}\n" + + "final class A implements I {}\n" + + "final class B implements I {}\n" + + "final class C implements I {}", + }, + libPath, + JavaCore.VERSION_21); + this.verifier.execute("p.X", new String[] {OUTPUT_DIR + File.separator + "bin", libPath}, new String[0], null); + assertEquals("Incorrect output", "OKEND", this.verifier.getExecutionOutput()); + } + } \ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java index 11f9d7cf451..bdc0abd2bc1 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java @@ -7882,7 +7882,7 @@ public void testIssue2319() { } // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2503 // [Switch Expression] Switching on sealed interface instance compiles even when the switch expression does not cover all possible input values - public void _testIssue2503() { + public void testIssue2503() { runNegativeTest( new String[] { "X.java", @@ -7911,7 +7911,7 @@ public static void main(String[] args) { } // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2503 // [Switch Expression] Switching on sealed interface instance compiles even when the switch expression does not cover all possible input values - public void _testIssue2503_2() { + public void testIssue2503_2() { runNegativeTest( new String[] { "X.java", @@ -7933,11 +7933,48 @@ public static void main(String[] args) { }, "----------\n" + "1. ERROR in X.java (at line 9)\n" - + " System.out.println(switch((I) new J()) {\n" - + " ^^^^^^^^^^^\n" + + " System.out.println(switch((I) new J() {}) {\n" + + " ^^^^^^^^^^^^^^\n" + "A switch expression should have a default case\n" + "----------\n"); } + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2503 + // [Switch Expression] Switching on sealed interface instance compiles even when the switch expression does not cover all possible input values + public void testIssue2503_3() { + runNegativeTest( + new String[] { + "X.java", + """ + sealed interface Outer permits Inner, Foo {} + sealed interface Inner extends Outer { + public static record A() implements Inner {} + public static record B() implements Inner {} + } + non-sealed interface Foo extends Outer {} + public class X { + public static void main(String[] args) { + Outer element = new FooImpl(); + + String test = switch(element) { + case Inner.A a -> "a"; + case Inner.B b -> "b"; + }; + + System.out.println(test); + } + + private static record FooImpl() implements Foo {} + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 11)\n" + + " String test = switch(element) {\n" + + " ^^^^^^^\n" + + "A switch expression should have a default case\n" + + "----------\n"); + } + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2508 // [Switch expression] Compiler erroneously treats guarded case patterns as covering switch selector type public void testIssue2508() { diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSealedTypeTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSealedTypeTests.java new file mode 100644 index 00000000000..bd2a7fa9995 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSealedTypeTests.java @@ -0,0 +1,133 @@ +/******************************************************************************* +* Copyright (c) 2024 Advantest Europe GmbH and others. +* +* This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Srikanth Sankaran - initial implementation +*******************************************************************************/ + +package org.eclipse.jdt.core.tests; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.eclipse.jdt.core.tests.builder.IncrementalTests; +import org.eclipse.jdt.core.tests.compiler.regression.BatchCompilerTest_15; +import org.eclipse.jdt.core.tests.compiler.regression.ClassFileReaderTest_17; +import org.eclipse.jdt.core.tests.compiler.regression.NegativeTypeAnnotationTest; +import org.eclipse.jdt.core.tests.compiler.regression.NullAnnotationTests21; +import org.eclipse.jdt.core.tests.compiler.regression.PatternMatching16Test; +import org.eclipse.jdt.core.tests.compiler.regression.RecordPatternProjectTest; +import org.eclipse.jdt.core.tests.compiler.regression.RecordPatternTest; +import org.eclipse.jdt.core.tests.compiler.regression.RecordsRestrictedClassTest; +import org.eclipse.jdt.core.tests.compiler.regression.ScannerTest; +import org.eclipse.jdt.core.tests.compiler.regression.SealedTypesTests; +import org.eclipse.jdt.core.tests.compiler.regression.SwitchPatternTest; +import org.eclipse.jdt.core.tests.compiler.regression.SwitchPatternTest21; +import org.eclipse.jdt.core.tests.compiler.regression.UnnamedPatternsAndVariablesTest; +import org.eclipse.jdt.core.tests.dom.ASTConverter_15Test; +import org.eclipse.jdt.core.tests.dom.ASTConverter_17Test; +import org.eclipse.jdt.core.tests.dom.ASTTest; +import org.eclipse.jdt.core.tests.dom.ConverterTestSetup; +import org.eclipse.jdt.core.tests.formatter.FormatterBugsTests; +import org.eclipse.jdt.core.tests.formatter.FormatterRegressionTests; +import org.eclipse.jdt.core.tests.model.CompletionTests16_1; +import org.eclipse.jdt.core.tests.model.CompletionTests17; +import org.eclipse.jdt.core.tests.model.Java21ElementTests; +import org.eclipse.jdt.core.tests.model.JavaSearchBugs15Tests; +import org.eclipse.jdt.core.tests.model.JavaSearchBugs17Tests; +import org.eclipse.jdt.core.tests.model.JavaSearchBugs19Tests; +import org.eclipse.jdt.core.tests.model.ReconcilerTests; +import org.eclipse.jdt.core.tests.model.ReconcilerTests21; +import org.eclipse.jdt.core.tests.model.ResolveTests21; +import org.eclipse.jdt.core.tests.model.SealedTypeModelTests; +import org.eclipse.jdt.core.tests.model.TypeHierarchyTests; +import org.eclipse.jdt.core.tests.rewrite.describing.ASTRewritingTypeDeclTest; +import org.eclipse.jdt.core.tests.util.AbstractCompilerTest; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class RunVariousSealedTypeTests extends TestCase { + + public RunVariousSealedTypeTests(String name) { + super(name); + } + public static Class[] getAllTestClasses() { + return new Class[] { +// SealedTypeElementProcessor.class, + IncrementalTests.class, + BatchCompilerTest_15.class, + ClassFileReaderTest_17.class, + NegativeTypeAnnotationTest.class, + NullAnnotationTests21.class, + PatternMatching16Test.class, + RecordPatternProjectTest.class, + RecordPatternTest.class, + RecordsRestrictedClassTest.class, + ScannerTest.class, + SealedTypesTests.class, + SwitchPatternTest.class, + SwitchPatternTest21.class, + UnnamedPatternsAndVariablesTest.class, + ASTRewritingTypeDeclTest.class, + ASTConverter_15Test.class, + ASTConverter_17Test.class, + ASTTest.class, + FormatterBugsTests.class, + FormatterRegressionTests.class, + CompletionTests16_1.class, + CompletionTests17.class, + Java21ElementTests.class, + JavaSearchBugs15Tests.class, + JavaSearchBugs17Tests.class, + JavaSearchBugs19Tests.class, + ReconcilerTests.class, + ReconcilerTests21.class, + ResolveTests21.class, + SealedTypeModelTests.class, + TypeHierarchyTests.class, + }; + } + + public static Test suite() { + TestSuite ts = new TestSuite(RunVariousSwitchTests.class.getName()); + + Class[] testClasses = getAllTestClasses(); + addTestsToSuite(ts, testClasses); + + AbstractCompilerTest.setpossibleComplianceLevels(AbstractCompilerTest.F_1_8); + + return ts; + } + public static void addTestsToSuite(TestSuite suite, Class[] testClasses) { + + for (int i = 0; i < testClasses.length; i++) { + Class testClass = testClasses[i]; + // call the suite() method and add the resulting suite to the suite + try { + Method suiteMethod = testClass.getDeclaredMethod("suite", new Class[0]); //$NON-NLS-1$ + Test test = (Test)suiteMethod.invoke(null, new Object[0]); + suite.addTest(test); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.getTargetException().printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + } + protected void tearDown() throws Exception { + ConverterTestSetup.PROJECT_SETUP = false; + super.tearDown(); + } +} \ No newline at end of file