From eaff460a9348f868889de3058bb6ef212cabeb37 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:07:33 +0530 Subject: [PATCH] [Enhanced Switch] Wrong error message: Cannot switch on a value of type Integer... at levels that don't support enhanced switch (#3380) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3379 --- .../compiler/ast/SwitchStatement.java | 43 +++---- .../tests/compiler/regression/SwitchTest.java | 105 ++++++++++++++++++ 2 files changed, 118 insertions(+), 30 deletions(-) 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 c18f738f012..daa9872434f 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 @@ -1029,16 +1029,12 @@ void gatherLabelExpression(LabelExpression labelExpression) { public void resolve(BlockScope upperScope) { try { TypeBinding expressionType = this.expression.resolveType(upperScope); + if (expressionType != null && !expressionType.isValidBinding()) + expressionType = null; // fault-tolerance: ignore further type mismatch from label expressions CompilerOptions compilerOptions = upperScope.compilerOptions(); if (expressionType != null) { this.expression.computeConversion(upperScope, expressionType, expressionType); checkType: { - - if (!expressionType.isValidBinding()) { - expressionType = null; // fault-tolerance: ignore type mismatch from constants from hereon - break checkType; - } - if (expressionType.isBaseType()) { if (JavaFeature.PRIMITIVES_IN_PATTERNS.isSupported(compilerOptions)) this.isPrimitiveSwitch = true; @@ -1047,28 +1043,25 @@ public void resolve(BlockScope upperScope) { if (expressionType.isCompatibleWith(TypeBinding.INT)) break checkType; } - if (expressionType.id == TypeIds.T_JavaLangString || expressionType.isEnum() || upperScope.isBoxingCompatibleWith(expressionType, TypeBinding.INT)) break checkType; - - if (!JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(compilerOptions) || (expressionType.isBaseType() && expressionType.id != T_null && expressionType.id != T_void)) { + if (JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(compilerOptions) && (!expressionType.isBaseType() || expressionType.id == T_null || expressionType.id == T_void)) { + this.isNonTraditional = true; + } else { if (!this.isPrimitiveSwitch) { // when isPrimitiveSwitch is set it is approved above upperScope.problemReporter().incorrectSwitchType(this.expression, expressionType); expressionType = null; // fault-tolerance: ignore type mismatch from constants from hereon } - } else { - this.isNonTraditional = true; } } } this.scope = new BlockScope(upperScope); if (expressionType != null) - reserveSecretVariablesSlots(); + reserveSecretVariablesSlot(); else this.switchBits |= InvalidSelector; - if (this.statements != null) { preprocess(); // make a pass over the switch block and allocate vectors. LocalVariableBinding[] patternVariables = NO_VARIABLES; @@ -1096,18 +1089,10 @@ public void resolve(BlockScope upperScope) { if (expressionType != null) { if (!expressionType.isBaseType() && upperScope.isBoxingCompatibleWith(expressionType, TypeBinding.INT)) { - if (this.containsPatterns || this.containsNull) { - if (!JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(compilerOptions) || (expressionType.isBaseType() && expressionType.id != T_null && expressionType.id != T_void)) { - if (!this.isPrimitiveSwitch) { // when isPrimitiveSwitch is set it is approved above - upperScope.problemReporter().incorrectSwitchType(this.expression, expressionType); - this.switchBits |= InvalidSelector; - expressionType = null; // fault-tolerance: ignore type mismatch from constants from hereon - } - } - } else + if (!this.containsPatterns && !this.containsNull) this.expression.computeConversion(upperScope, TypeBinding.INT, expressionType); } - releaseUnusedSecretVariables(); + releaseUnusedSecretVariable(); complainIfNotExhaustiveSwitch(upperScope, expressionType, compilerOptions); } @@ -1148,12 +1133,10 @@ private void complainIfNotExhaustiveSwitch(BlockScope upperScope, TypeBinding se } if (this.defaultCase == null) { - if (this instanceof SwitchExpression // complained about elsewhere, don't also bark here - || compilerOptions.getSeverity(CompilerOptions.MissingDefaultCase) == ProblemSeverities.Ignore) { + if (this instanceof SwitchExpression || compilerOptions.getSeverity(CompilerOptions.MissingDefaultCase) == ProblemSeverities.Ignore) // complained about elsewhere, don't also bark here upperScope.methodScope().hasMissingSwitchDefault = true; - } else { + else upperScope.problemReporter().missingDefaultCase(this, true, selectorType); - } } return; } @@ -1311,13 +1294,13 @@ private boolean needPatternDispatchCopy() { return !(eType.isPrimitiveOrBoxedPrimitiveType() || eType.isEnum() || eType.id == TypeIds.T_JavaLangString); // classic selectors } - private void reserveSecretVariablesSlots() { // may be released later if unused. + private void reserveSecretVariablesSlot() { // may be released later if unused. this.selector = new LocalVariableBinding(SecretSelectorVariableName, this.scope.getJavaLangObject(), ClassFileConstants.AccDefault, false); this.scope.addLocalVariable(this.selector); this.selector.setConstant(Constant.NotAConstant); } - private void releaseUnusedSecretVariables() { + private void releaseUnusedSecretVariable() { if (this.selector != null) { if (this.expression.resolvedType.id == T_JavaLangString && !this.isNonTraditional) { this.selector.useFlag = LocalVariableBinding.USED; @@ -1325,7 +1308,7 @@ private void releaseUnusedSecretVariables() { } else if (needPatternDispatchCopy()) { this.selector.useFlag = LocalVariableBinding.USED; this.selector.type = this.expression.resolvedType; - } + } // else gets released by virtue of not being tagged USED. } } protected void reportMissingEnumConstantCase(BlockScope upperScope, FieldBinding enumConstant) { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchTest.java index b70952ed440..e3c4394ad36 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchTest.java @@ -3624,6 +3624,111 @@ String unreachableCode(String arg) { "----------\n"); } +// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3379 +// [Enhanced Switch] Wrong error message: Cannot switch on a value of type Integer... at levels that don't support enhanced switch +public void testIssue3379() throws Exception { + if (this.complianceLevel < ClassFileConstants.JDK14) + return; // uses switch rules. + String [] sources = new String[] { + "X.java", + """ + public class X { + public static void main(String[] args) { + Integer i = 42; + switch (i) { + case null -> System.out.println("Null"); + default -> System.out.println("Default: " + i); + } + } + } + """, + }; + + if (this.complianceLevel < ClassFileConstants.JDK21) { + this.runNegativeTest(sources, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " case null -> System.out.println(\"Null\");\n" + + " ^^^^\n" + + "The Java feature 'Pattern Matching in Switch' is only available with source level 21 and above\n" + + "----------\n"); + } else { + this.runConformTest(sources, "Default: 42"); + } +} + +// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3379 +// [Enhanced Switch] Wrong error message: Cannot switch on a value of type Integer... at levels that don't support enhanced switch +public void testIssue3379_2() throws Exception { + if (this.complianceLevel < ClassFileConstants.JDK14) + return; // uses switch rules. + String [] sources = new String[] { + "X.java", + """ + public class X { + public static void main(String[] args) { + Long i = 42l; + switch (i) { + case null -> System.out.println("Null"); + default -> System.out.println("Default: " + i); + } + } + } + """, + }; + + if (this.complianceLevel < ClassFileConstants.JDK21) { + this.runNegativeTest(sources, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " switch (i) {\n" + + " ^\n" + + "Cannot switch on a value of type Long. Only convertible int values, strings or enum variables are permitted\n" + + "----------\n" + + "2. ERROR in X.java (at line 5)\n" + + " case null -> System.out.println(\"Null\");\n" + + " ^^^^\n" + + "The Java feature 'Pattern Matching in Switch' is only available with source level 21 and above\n" + + "----------\n"); + } else { + this.runConformTest(sources, "Default: 42"); + } +} + +// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3379 +// [Enhanced Switch] Wrong error message: Cannot switch on a value of type Integer... at levels that don't support enhanced switch +public void testIssue3379_3() throws Exception { + if (this.complianceLevel < ClassFileConstants.JDK14) + return; // uses switch rules. + + String [] sources = new String[] { + "X.java", + """ + public class X { + public static void main(String[] args) { + Short i = 42; + switch (i) { + case null -> System.out.println("Null"); + default -> System.out.println("Default: " + i); + } + } + } + """, + }; + + if (this.complianceLevel < ClassFileConstants.JDK21) { + this.runNegativeTest(sources, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " case null -> System.out.println(\"Null\");\n" + + " ^^^^\n" + + "The Java feature 'Pattern Matching in Switch' is only available with source level 21 and above\n" + + "----------\n"); + } else { + this.runConformTest(sources, "Default: 42"); + } +} + public static Class testClass() { return SwitchTest.class; }