Bug 575241 - [17] switch block covering a type T
Change-Id: If2bd7343d1c8c6562f00b4695edd54c7aff86a71
Signed-off-by: Manoj Palat <manpalat@in.ibm.com>
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/183890
Tested-by: JDT Bot <jdt-bot@eclipse.org>
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 fff3ce0..8435308 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
@@ -1635,6 +1635,11 @@
"----------\n" +
"1. ERROR in X.java (at line 5)\n" +
" case 1, Integer i -> System.out.println(o);\n" +
+ " ^\n" +
+ "The constant case label element is not compatible with switch expression type Integer\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 5)\n" +
+ " case 1, Integer i -> System.out.println(o);\n" +
" ^^^^^^^^^\n" +
"Constant case label elements and pattern case label elements cannot be present in a switch label\n" +
"----------\n");
@@ -1956,10 +1961,15 @@
"----------\n" +
"1. ERROR in X.java (at line 7)\n" +
" case 10, null, var k -> System.out.println(0);\n" +
+ " ^^\n" +
+ "The constant case label element is not compatible with switch expression type Integer\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 7)\n" +
+ " case 10, null, var k -> System.out.println(0);\n" +
" ^^^\n" +
"\'var\' is not allowed here\n" +
"----------\n" +
- "2. ERROR in X.java (at line 7)\n" +
+ "3. ERROR in X.java (at line 7)\n" +
" case 10, null, var k -> System.out.println(0);\n" +
" ^^^^^\n" +
"Constant case label elements and pattern case label elements cannot be present in a switch label\n" +
@@ -2055,20 +2065,25 @@
"----------\n" +
"1. ERROR in X.java (at line 7)\n" +
" case default, 1, var k -> System.out.println(0);\n" +
- " ^^^\n" +
- "\'var\' is not allowed here\n" +
+ " ^\n" +
+ "The constant case label element is not compatible with switch expression type Integer\n" +
"----------\n" +
"2. ERROR in X.java (at line 7)\n" +
" case default, 1, var k -> System.out.println(0);\n" +
- " ^^^^^\n" +
- "A switch label may not have both a pattern case label element and a default case label element.\n" +
+ " ^^^\n" +
+ "\'var\' is not allowed here\n" +
"----------\n" +
"3. ERROR in X.java (at line 7)\n" +
" case default, 1, var k -> System.out.println(0);\n" +
" ^^^^^\n" +
+ "A switch label may not have both a pattern case label element and a default case label element.\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 7)\n" +
+ " case default, 1, var k -> System.out.println(0);\n" +
+ " ^^^^^\n" +
"Constant case label elements and pattern case label elements cannot be present in a switch label\n" +
"----------\n" +
- "4. ERROR in X.java (at line 8)\n" +
+ "5. ERROR in X.java (at line 8)\n" +
" default -> System.out.println(o);\n" +
" ^^^^^^^\n" +
"The default case is already defined\n" +
@@ -2905,4 +2920,157 @@
},
"0");
}
-}
+ public void testBug575241_01() {
+ runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n"+
+ " static int foo(Integer i) {\n"+
+ " return switch (i) {\n"+
+ " case Integer i1 -> 0;\n"+
+ " };\n"+
+ " }\n"+
+ " public static void main(String[] args) {\n"+
+ " System.out.println(foo(1));\n"+
+ " }\n"+
+ "}",
+ },
+ "0");
+ }
+ public void testBug575241_02() {
+ runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n"+
+ " static int foo(Integer i) {\n"+
+ " return switch (i) {\n"+
+ " case Object o -> 0;\n"+
+ " };\n"+
+ " }\n"+
+ " public static void main(String[] args) {\n"+
+ " System.out.println(foo(1));\n"+
+ " }\n"+
+ "}",
+ },
+ "0");
+ }
+ public void testBug575241_03() {
+ runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n"+
+ " static int foo(Object myVar) {\n"+
+ " return switch (myVar) {\n"+
+ " case null -> 0;\n"+
+ " case Integer o -> 1;\n"+
+ " case Object obj ->2;\n"+
+ " };\n"+
+ " }\n"+
+ " public static void main(String[] args) {\n"+
+ " System.out.println(foo(Integer.valueOf(0)));\n"+
+ " System.out.println(foo(null));\n"+
+ " }\n"+
+ "}",
+ },
+ "1\n" +
+ "0");
+ }
+ public void testBug575241_04() {
+ runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n"+
+ " static int foo(Object myVar) {\n"+
+ " return switch (myVar) {\n"+
+ " case Integer o -> 1;\n"+
+ " case Object obj ->2;\n"+
+ " };\n"+
+ " }\n"+
+ " public static void main(String[] args) {\n"+
+ " System.out.println(foo(Integer.valueOf(0)));\n"+
+ " System.out.println(foo(null));\n"+
+ " }\n"+
+ "}",
+ },
+ "1\n" +
+ "2");
+ }
+ public void testBug575241_05() {
+ runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n"+
+ " static void foo(Integer myVar) {\n"+
+ " switch (myVar) {\n"+
+ " case null -> System.out.println(100);\n"+
+ " case Integer o -> System.out.println(o);\n"+
+ " };\n"+
+ " }\n"+
+ " public static void main(String[] args) {\n"+
+ " foo(Integer.valueOf(0));\n"+
+ " foo(null);\n"+
+ " }\n"+
+ "}",
+ },
+ "0\n" +
+ "100");
+ }
+ public void testBug575241_06() {
+ runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n"+
+ " static void foo(Integer myVar) {\n"+
+ " switch (myVar) {\n"+
+ " case Integer o -> System.out.println(o);\n"+
+ " };\n"+
+ " }\n"+
+ " public static void main(String[] args) {\n"+
+ " foo(Integer.valueOf(0));\n"+
+ " foo(null);\n"+
+ " }\n"+
+ "}",
+ },
+ "0\n" +
+ "null");
+ }
+ public void testBug575241_07() {
+ runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n"+
+ " static void foo(String myVar) {\n"+
+ " switch (myVar) {\n"+
+ " case null -> System.out.println(100);\n"+
+ " case String o -> System.out.println(o);\n"+
+ " };\n"+
+ " }\n"+
+ " public static void main(String[] args) {\n"+
+ " foo(\"Hello\");\n"+
+ " foo(null);\n"+
+ " }\n"+
+ "}",
+ },
+ "Hello\n" +
+ "100");
+ }
+ public void testBug575241_08() {
+ runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n"+
+ " static void foo(String myVar) {\n"+
+ " switch (myVar) {\n"+
+ " case String o -> System.out.println(o);\n"+
+ " };\n"+
+ " }\n"+
+ " public static void main(String[] args) {\n"+
+ " foo(\"Hello\");\n"+
+ " foo(null);\n"+
+ " }\n"+
+ "}",
+ },
+ "Hello\n" +
+ "null");
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java
index 24895e5..12dd6a5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java
@@ -419,9 +419,13 @@
}
}
if (e.isTotalForType(expressionType)) {
- switchStatement.switchBits |= SwitchStatement.TotalPattern;
+ switchStatement.switchBits |= (SwitchStatement.TotalPattern | SwitchStatement.Covered);
if (switchStatement.defaultCase != null)
scope.problemReporter().illegalTotalPatternWithDefault(this);
+ switchStatement.totalPattern = e;
+ e.isTotalTypeNode = true;
+ if (switchStatement.nullCase == null)
+ constant = IntConstant.fromValue(-1);
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Pattern.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Pattern.java
index ac4b862..0218755 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Pattern.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Pattern.java
@@ -25,6 +25,8 @@
public abstract class Pattern extends Expression {
+ /* package */ boolean isTotalTypeNode = false;
+
public boolean isTotalForType(TypeBinding type) {
return false;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java
index b1bf615..ad8f599 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java
@@ -71,6 +71,7 @@
public boolean containsPatterns;
private BranchLabel switchPatternRestartTarget;
+ /* package */ Pattern totalPattern;
// fallthrough
public final static int CASE = 0;
@@ -82,6 +83,7 @@
public final static int LabeledRules = ASTNode.Bit1;
public final static int NullCase = ASTNode.Bit2;
public final static int TotalPattern = ASTNode.Bit3;
+ public final static int Covered = ASTNode.Bit4;
// for switch on strings
private static final char[] SecretStringVariableName = " switchDispatchString".toCharArray(); //$NON-NLS-1$
@@ -441,6 +443,8 @@
Expression e = stmt.constantExpressions[k];
if (e instanceof FakeDefaultLiteral) continue;
targetLabels[count++] = (caseLabels[j] = newLabel.apply(codeStream));
+ if (this.totalPattern != null && e.equals(this.totalPattern))
+ this.defaultCase = stmt;
caseLabels[j++].tagBits |= BranchLabel.USED;
}
System.arraycopy(targetLabels, 0, stmt.targetLabels = new BranchLabel[count], 0, count);
@@ -653,7 +657,7 @@
}
private void generateCodeSwitchPatternPrologue(BlockScope currentScope, CodeStream codeStream) {
this.expression.generateCode(currentScope, codeStream, true);
- if ((this.switchBits & NullCase) == 0) {
+ if ((this.switchBits & NullCase) == 0 && this.totalPattern == null) {
codeStream.dup();
codeStream.invokeJavaUtilObjectsrequireNonNull();
codeStream.pop();
@@ -771,7 +775,7 @@
upperScope.problemReporter().incorrectSwitchType(this.expression, expressionType); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=360317
}
break checkType;
- } else if (upperScope.isBoxingCompatibleWith(expressionType, TypeBinding.INT)) {
+ } else if (!this.containsPatterns && upperScope.isBoxingCompatibleWith(expressionType, TypeBinding.INT)) {
this.expression.computeConversion(upperScope, TypeBinding.INT, expressionType);
break checkType;
} else if (compilerOptions.complianceLevel >= ClassFileConstants.JDK1_7 && expressionType.id == TypeIds.T_JavaLangString) {
@@ -791,7 +795,7 @@
}
}
}
- if (isStringSwitch) {
+ if (isStringSwitch) {
// the secret variable should be created before iterating over the switch's statements that could
// create more locals. This must be done to prevent overlapping of locals
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=356002
@@ -892,19 +896,15 @@
}
}
- boolean checkSealed = this.containsPatterns
- && JavaFeature.SEALED_CLASSES.isSupported(compilerOptions)
- && JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(compilerOptions)
- && this.expression.resolvedType instanceof ReferenceBinding
- && ((ReferenceBinding) this.expression.resolvedType).isSealed();
// check default case for all kinds of switch:
+ checkAndFlagDefaultSealed(upperScope, compilerOptions);
if (this.defaultCase == null) {
if (ignoreMissingDefaultCase(compilerOptions, isEnumSwitch)) {
if (isEnumSwitch) {
upperScope.methodScope().hasMissingSwitchDefault = true;
}
} else {
- if (!checkSealed)
+ if (!isCovered())
upperScope.problemReporter().missingDefaultCase(this, isEnumSwitch, expressionType);
}
}
@@ -923,6 +923,7 @@
if ((enumConstant.id + 1) == this.constants[j]) // zero should not be returned see bug 141810
break findConstant;
}
+ this.switchBits &= ~(1 << SwitchStatement.Covered);
// enum constant did not get referenced from switch
boolean suppress = (this.defaultCase != null && (this.defaultCase.bits & DocumentedCasesOmitted) != 0);
if (!suppress) {
@@ -932,17 +933,27 @@
}
}
}
- } else if (checkSealed) {
- checkAndFlagDefaultSealed(upperScope);
}
} finally {
if (this.scope != null) this.scope.enclosingCase = null; // no longer inside switch case block
}
}
- private void checkAndFlagDefaultSealed(BlockScope skope) {
- if (this.defaultCase != null) return;
+ private boolean isCovered() {
+ return (this.switchBits & SwitchStatement.Covered) != 0;
+ }
+ private void checkAndFlagDefaultSealed(BlockScope skope, CompilerOptions compilerOptions) {
+ if (this.defaultCase != null) { // mark covered as a side effect (since covers is intro in 406)
+ this.switchBits |= SwitchStatement.Covered;
+ return;
+ }
+ boolean checkSealed = this.containsPatterns
+ && JavaFeature.SEALED_CLASSES.isSupported(compilerOptions)
+ && JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(compilerOptions)
+ && this.expression.resolvedType instanceof ReferenceBinding
+ && ((ReferenceBinding) this.expression.resolvedType).isSealed();
+ if (!checkSealed) return;
ReferenceBinding ref = (ReferenceBinding) this.expression.resolvedType;
- assert ref.isSealed();
+ if (!ref.isSealed()) return;
List<TypeBinding> permittedTypes = Arrays.asList(ref.permittedTypes());
for (TypeBinding pt : permittedTypes) {
if (!this.caseLabelElementTypes.contains(pt)) {
@@ -950,11 +961,13 @@
return;
}
}
+ this.switchBits |= SwitchStatement.Covered;
}
private void addSecretPatternSwitchVariables(BlockScope upperScope) {
if (this.containsPatterns) {
this.scope = new BlockScope(upperScope);
- this.dispatchPatternCopy = new LocalVariableBinding(SecretPatternVariableName, upperScope.getJavaLangObject(), ClassFileConstants.AccDefault, false);
+ TypeBinding type = this.expression.resolvedType.clone(this.expression.resolvedType.enclosingType());
+ this.dispatchPatternCopy = new LocalVariableBinding(SecretPatternVariableName, type, ClassFileConstants.AccDefault, false);
this.scope.addLocalVariable(this.dispatchPatternCopy);
this.dispatchPatternCopy.setConstant(Constant.NotAConstant);
this.dispatchPatternCopy.useFlag = LocalVariableBinding.USED;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypePattern.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypePattern.java
index def9ad4..2e7d39f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypePattern.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypePattern.java
@@ -67,7 +67,8 @@
public void generateCode(BlockScope currentScope, CodeStream codeStream) {
if (this.local != null) {
LocalVariableBinding localBinding = this.local.binding;
- codeStream.checkcast(localBinding.type);
+ if (!this.isTotalTypeNode)
+ codeStream.checkcast(localBinding.type);
this.local.generateCode(currentScope, codeStream);
codeStream.store(localBinding, false);
localBinding.recordInitializationStartPC(codeStream.position);