@@ -160,6 +160,20 @@ enum CaseQualifications {
160
160
SOME_OR_ALL_CASES_DONT_QUALIFY
161
161
}
162
162
163
+ /**
164
+ * The kind of null/default cases included within a single CaseTree.
165
+ *
166
+ * <p>This enum is used to classify whether a CaseTree includes a null and/or default. Referencing
167
+ * JLS 21 §14.11.1, the `SwitchLabel:` production has specific rules applicable to null/default
168
+ * cases: `case null, [default]` and `default`. All other scenarios are lumped into KIND_NEITHER.
169
+ */
170
+ enum NullDefaultKind {
171
+ KIND_NULL_AND_DEFAULT ,
172
+ KIND_DEFAULT ,
173
+ KIND_NULL ,
174
+ KIND_NEITHER
175
+ }
176
+
163
177
private final boolean enableDirectConversion ;
164
178
private final boolean enableReturnSwitchConversion ;
165
179
private final boolean enableAssignmentSwitchConversion ;
@@ -293,12 +307,15 @@ private static AnalysisResult analyzeSwitchTree(SwitchTree switchTree, VisitorSt
293
307
// Case patterns are not currently supported by the checker.
294
308
return DEFAULT_ANALYSIS_RESULT ;
295
309
}
296
- boolean isDefaultCase = caseTree .getExpressions ().isEmpty ();
310
+
311
+ NullDefaultKind nullDefaultKind = analyzeCaseForNullAndDefault (caseTree );
312
+ boolean isDefaultCase =
313
+ nullDefaultKind .equals (NullDefaultKind .KIND_DEFAULT )
314
+ || nullDefaultKind .equals (NullDefaultKind .KIND_NULL_AND_DEFAULT );
297
315
isNullCase .set (
298
316
caseIndex ,
299
- !isDefaultCase
300
- && caseTree .getExpressions ().stream ()
301
- .anyMatch (expressionTree -> expressionTree .getKind () == Kind .NULL_LITERAL ));
317
+ nullDefaultKind .equals (NullDefaultKind .KIND_NULL )
318
+ || nullDefaultKind .equals (NullDefaultKind .KIND_NULL_AND_DEFAULT ));
302
319
hasDefaultCase |= isDefaultCase ;
303
320
304
321
// Null case can never be grouped with a preceding case
@@ -606,6 +623,21 @@ private static String renderJavaSourceOfAssignment(ExpressionTree tree) {
606
623
return pretty .operatorName (jcAssignOp .getTag ().noAssignOp ()) + EQUALS_STRING ;
607
624
}
608
625
626
+ /**
627
+ * Renders the Java source prefix needed for the supplied {@code nullDefaultKind}, incorporating
628
+ * whether the `default` case should be removed.
629
+ */
630
+ private static String renderNullDefaultKindPrefix (
631
+ NullDefaultKind nullDefaultKind , boolean removeDefault ) {
632
+
633
+ return switch (nullDefaultKind ) {
634
+ case KIND_NULL_AND_DEFAULT -> removeDefault ? "case null" : "case null, default" ;
635
+ case KIND_NULL -> "case null" ;
636
+ case KIND_DEFAULT -> removeDefault ? "" : "default" ;
637
+ case KIND_NEITHER -> "case " ;
638
+ };
639
+ }
640
+
609
641
/**
610
642
* Analyze a single {@code case} of a single {@code switch} statement to determine whether it is
611
643
* convertible to a return switch. The supplied {@code previousCaseQualifications} is updated and
@@ -951,10 +983,10 @@ private static SuggestedFix convertDirectlyToExpressionSwitch(
951
983
boolean firstCaseInGroup = true ;
952
984
for (int caseIndex = 0 ; caseIndex < cases .size (); caseIndex ++) {
953
985
CaseTree caseTree = cases .get (caseIndex );
954
- boolean isDefaultCase = isSwitchDefault (caseTree );
986
+ NullDefaultKind nullDefaultKind = analyzeCaseForNullAndDefault (caseTree );
955
987
956
- if (removeDefault && isDefaultCase ) {
957
- // Skip default case
988
+ if (removeDefault && nullDefaultKind . equals ( NullDefaultKind . KIND_DEFAULT ) ) {
989
+ // Skip removed default (and its code) entirely
958
990
continue ;
959
991
}
960
992
@@ -971,14 +1003,19 @@ private static SuggestedFix convertDirectlyToExpressionSwitch(
971
1003
? extractCommentsBeforeFirstCase (switchTree , allSwitchComments ).orElse ("" )
972
1004
: "" );
973
1005
974
- replacementCodeBuilder .append ("\n " );
975
- if (!isDefaultCase ) {
976
- replacementCodeBuilder .append ("case " );
1006
+ replacementCodeBuilder
1007
+ .append ("\n " )
1008
+ .append (renderNullDefaultKindPrefix (nullDefaultKind , removeDefault ));
1009
+ } else {
1010
+ // Second or later case in our group
1011
+ if (nullDefaultKind .equals (NullDefaultKind .KIND_DEFAULT )) {
1012
+ replacementCodeBuilder .append ("default" );
977
1013
}
978
1014
}
979
- replacementCodeBuilder .append (
980
- isDefaultCase ? "default" : printCaseExpressions (caseTree , state ));
981
1015
1016
+ if (nullDefaultKind .equals (NullDefaultKind .KIND_NEITHER )) {
1017
+ replacementCodeBuilder .append (printCaseExpressions (caseTree , state ));
1018
+ }
982
1019
Optional <String > commentsAfterCaseOptional =
983
1020
extractCommentsAfterCase (switchTree , allSwitchComments , state , caseIndex );
984
1021
if (analysisResult .groupedWithNextCase ().get (caseIndex )) {
@@ -1095,9 +1132,9 @@ private static SuggestedFix convertToReturnSwitch(
1095
1132
boolean firstCaseInGroup = true ;
1096
1133
for (int caseIndex = 0 ; caseIndex < cases .size (); caseIndex ++) {
1097
1134
CaseTree caseTree = cases .get (caseIndex );
1098
- boolean isDefaultCase = isSwitchDefault (caseTree );
1099
- if (removeDefault && isDefaultCase ) {
1100
- // Skip default case
1135
+ NullDefaultKind nullDefaultKind = analyzeCaseForNullAndDefault (caseTree );
1136
+ if (removeDefault && nullDefaultKind . equals ( NullDefaultKind . KIND_DEFAULT ) ) {
1137
+ // Skip removed default (and its code) entirely
1101
1138
continue ;
1102
1139
}
1103
1140
@@ -1111,13 +1148,19 @@ private static SuggestedFix convertToReturnSwitch(
1111
1148
? extractCommentsBeforeFirstCase (switchTree , allSwitchComments ).orElse ("" )
1112
1149
: "" );
1113
1150
1114
- replacementCodeBuilder .append ("\n " );
1115
- if (!isDefaultCase ) {
1116
- replacementCodeBuilder .append ("case " );
1151
+ replacementCodeBuilder
1152
+ .append ("\n " )
1153
+ .append (renderNullDefaultKindPrefix (nullDefaultKind , removeDefault ));
1154
+ } else {
1155
+ // Second or later case in our group
1156
+ if (nullDefaultKind .equals (NullDefaultKind .KIND_DEFAULT )) {
1157
+ replacementCodeBuilder .append ("default" );
1117
1158
}
1118
1159
}
1119
- replacementCodeBuilder .append (
1120
- isDefaultCase ? "default" : printCaseExpressions (caseTree , state ));
1160
+
1161
+ if (nullDefaultKind .equals (NullDefaultKind .KIND_NEITHER )) {
1162
+ replacementCodeBuilder .append (printCaseExpressions (caseTree , state ));
1163
+ }
1121
1164
1122
1165
Optional <String > commentsAfterCaseOptional =
1123
1166
extractCommentsAfterCase (switchTree , allSwitchComments , state , caseIndex );
@@ -1323,9 +1366,10 @@ private static SuggestedFix convertToAssignmentSwitch(
1323
1366
boolean firstCaseInGroup = true ;
1324
1367
for (int caseIndex = 0 ; caseIndex < cases .size (); caseIndex ++) {
1325
1368
CaseTree caseTree = cases .get (caseIndex );
1326
- boolean isDefaultCase = isSwitchDefault (caseTree );
1327
- if (removeDefault && isDefaultCase ) {
1328
- // Remove `default:` case (and its code, if any) from the SuggestedFix
1369
+ NullDefaultKind nullDefaultKind = analyzeCaseForNullAndDefault (caseTree );
1370
+
1371
+ if (removeDefault && nullDefaultKind .equals (NullDefaultKind .KIND_DEFAULT )) {
1372
+ // Skip removed default (and its code) entirely
1329
1373
continue ;
1330
1374
}
1331
1375
ImmutableList <StatementTree > filteredStatements = filterOutRedundantBreak (caseTree );
@@ -1340,13 +1384,19 @@ private static SuggestedFix convertToAssignmentSwitch(
1340
1384
? extractCommentsBeforeFirstCase (switchTree , allSwitchComments ).orElse ("" )
1341
1385
: "" );
1342
1386
1343
- replacementCodeBuilder .append ("\n " );
1344
- if (!isDefaultCase ) {
1345
- replacementCodeBuilder .append ("case " );
1387
+ replacementCodeBuilder
1388
+ .append ("\n " )
1389
+ .append (renderNullDefaultKindPrefix (nullDefaultKind , removeDefault ));
1390
+ } else {
1391
+ // Second or later case in our group
1392
+ if (nullDefaultKind .equals (NullDefaultKind .KIND_DEFAULT )) {
1393
+ replacementCodeBuilder .append ("default" );
1346
1394
}
1347
1395
}
1348
- replacementCodeBuilder .append (
1349
- isDefaultCase ? "default" : printCaseExpressions (caseTree , state ));
1396
+
1397
+ if (nullDefaultKind .equals (NullDefaultKind .KIND_NEITHER )) {
1398
+ replacementCodeBuilder .append (printCaseExpressions (caseTree , state ));
1399
+ }
1350
1400
1351
1401
Optional <String > commentsAfterCaseOptional =
1352
1402
extractCommentsAfterCase (switchTree , allSwitchComments , state , caseIndex );
@@ -1778,6 +1828,26 @@ private static String transformAssignOrThrowBlock(
1778
1828
return transformedBlockBuilder .toString ();
1779
1829
}
1780
1830
1831
+ /**
1832
+ * Determines whether the supplied {@code caseTree} case contains `case null` and/or `default`.
1833
+ */
1834
+ private static NullDefaultKind analyzeCaseForNullAndDefault (CaseTree caseTree ) {
1835
+ boolean hasDefault = isSwitchDefault (caseTree );
1836
+ boolean hasNull =
1837
+ caseTree .getExpressions ().stream ()
1838
+ .anyMatch (expression -> expression .getKind ().equals (Tree .Kind .NULL_LITERAL ));
1839
+
1840
+ if (hasNull && hasDefault ) {
1841
+ return NullDefaultKind .KIND_NULL_AND_DEFAULT ;
1842
+ } else if (hasNull ) {
1843
+ return NullDefaultKind .KIND_NULL ;
1844
+ } else if (hasDefault ) {
1845
+ return NullDefaultKind .KIND_DEFAULT ;
1846
+ }
1847
+
1848
+ return NullDefaultKind .KIND_NEITHER ;
1849
+ }
1850
+
1781
1851
@ AutoValue
1782
1852
abstract static class AnalysisResult {
1783
1853
/** Whether the statement switch can be directly converted to an expression switch */
0 commit comments