2222import java .util .Deque ;
2323import java .util .LinkedList ;
2424import java .util .List ;
25+ import java .util .Set ;
2526import javax .annotation .Nullable ;
2627import org .sonar .check .Rule ;
2728import org .sonar .java .checks .helpers .QuickFixHelper ;
@@ -49,6 +50,10 @@ public class PatternMatchUsingIfCheck extends IssuableSubscriptionVisitor implem
4950
5051 private static final String ISSUE_MESSAGE = "Replace the chain of if/else with a switch expression." ;
5152 private static final int INDENT = 2 ;
53+ private static final Set <String > SCRUTINEE_TYPES_FOR_NON_PATTERN_SWITCH = Set .of (
54+ "byte" , "short" , "char" , "int" ,
55+ "java.lang.Byte" , "java.lang.Short" , "java.lang.Character" , "java.lang.Integer"
56+ );
5257
5358 @ Override
5459 public boolean isCompatibleWithJavaVersion (JavaVersion version ) {
@@ -74,7 +79,8 @@ public void visitNode(Tree tree) {
7479 }
7580
7681 var cases = extractCasesFromIfSequence (topLevelIfStat );
77- if (cases == null || !(cases .get (cases .size () - 1 ) instanceof DefaultCase ) || !casesHaveCommonScrutinee (cases )) {
82+ if (cases == null || !(cases .get (cases .size () - 1 ) instanceof DefaultCase ) || !casesHaveCommonScrutinee (cases )
83+ || (cases .get (0 ) instanceof EqualityCase && !hasValidScrutineeTypeForNonPatternSwitch (cases .get (0 ).scrutinee ()))) {
7884 return ;
7985 }
8086
@@ -86,7 +92,15 @@ public void visitNode(Tree tree) {
8692 }
8793
8894 private static boolean casesHaveCommonScrutinee (List <Case > cases ) {
89- return cases .stream ().allMatch (c -> c .scrutinee ().equals (cases .get (0 ).scrutinee ()));
95+ return cases .stream ().allMatch (c -> c .scrutinee ().name ().equals (cases .get (0 ).scrutinee ().name ()));
96+ }
97+
98+ private static boolean hasValidScrutineeTypeForNonPatternSwitch (IdentifierTree scrutinee ) {
99+ if (scrutinee .symbolType ().symbol ().isEnum ()) {
100+ return true ;
101+ }
102+ var fullyQualifiedTypeName = scrutinee .symbolType ().fullyQualifiedName ();
103+ return SCRUTINEE_TYPES_FOR_NON_PATTERN_SWITCH .contains (fullyQualifiedTypeName );
90104 }
91105
92106 private static @ Nullable List <Case > extractCasesFromIfSequence (IfStatementTree topLevelIfStat ) {
@@ -111,7 +125,7 @@ private static boolean casesHaveCommonScrutinee(List<Case> cases) {
111125 populateGuardsList (condition , guards );
112126 if (leftmost instanceof PatternInstanceOfTree patInstOf && patInstOf .pattern () != null
113127 && patInstOf .expression () instanceof IdentifierTree idTree ) {
114- return new PatternMatchCase (idTree . name () , patInstOf .pattern (), guards , body );
128+ return new PatternMatchCase (idTree , patInstOf .pattern (), guards , body );
115129 } else if ((leftmost .kind () == Kind .CONDITIONAL_OR || leftmost .kind () == Kind .EQUAL_TO ) && guards .isEmpty ()) {
116130 return buildEqualityCase (leftmost , body );
117131 } else {
@@ -124,35 +138,35 @@ private static boolean casesHaveCommonScrutinee(List<Case> cases) {
124138 */
125139 private static @ Nullable EqualityCase buildEqualityCase (ExpressionTree expr , StatementTree body ) {
126140 var constantsList = new LinkedList <ExpressionTree >();
127- String scrutinee = null ;
141+ IdentifierTree scrutinee = null ;
128142 while (expr .kind () == Kind .CONDITIONAL_OR ) {
129143 var binary = (BinaryExpressionTree ) expr ;
130144 var varAndCst = extractVarAndConstFromEqualityCheck (binary .rightOperand ());
131145 if (varAndCst == null ) {
132146 return null ;
133147 } else if (scrutinee == null ) {
134148 scrutinee = varAndCst .a ;
135- } else if (!varAndCst .a .equals (scrutinee )) {
149+ } else if (!varAndCst .a .name (). equals (scrutinee . name () )) {
136150 return null ;
137151 }
138152 constantsList .addFirst (varAndCst .b );
139153 expr = binary .leftOperand ();
140154 }
141155 var varAndCst = extractVarAndConstFromEqualityCheck (expr );
142- if (varAndCst == null || (scrutinee != null && !varAndCst .a .equals (scrutinee ))) {
156+ if (varAndCst == null || (scrutinee != null && !varAndCst .a .name (). equals (scrutinee . name () ))) {
143157 return null ;
144158 }
145159 constantsList .addFirst (varAndCst .b );
146160 return new EqualityCase (scrutinee == null ? varAndCst .a : scrutinee , constantsList , body );
147161 }
148162
149- private static @ Nullable Pair <String , ExpressionTree > extractVarAndConstFromEqualityCheck (ExpressionTree expr ) {
163+ private static @ Nullable Pair <IdentifierTree , ExpressionTree > extractVarAndConstFromEqualityCheck (ExpressionTree expr ) {
150164 if (expr .kind () == Kind .EQUAL_TO ) {
151165 var binary = (BinaryExpressionTree ) expr ;
152166 if (binary .leftOperand () instanceof IdentifierTree idTree && isPossibleConstantForCase (binary .rightOperand ())) {
153- return new Pair <>(idTree . name () , binary .rightOperand ());
167+ return new Pair <>(idTree , binary .rightOperand ());
154168 } else if (binary .rightOperand () instanceof IdentifierTree idTree && isPossibleConstantForCase (binary .leftOperand ())) {
155- return new Pair <>(idTree . name () , binary .leftOperand ());
169+ return new Pair <>(idTree , binary .leftOperand ());
156170 }
157171 }
158172 return null ;
@@ -191,13 +205,16 @@ private JavaQuickFix computeQuickFix(List<Case> cases, IfStatementTree topLevelI
191205 if (canLiftReturn ) {
192206 sb .append ("return " );
193207 }
194- sb .append ("switch (" ).append (cases .get (0 ).scrutinee ()).append (") {\n " );
208+ sb .append ("switch (" ).append (cases .get (0 ).scrutinee (). name () ).append (") {\n " );
195209 for (Case caze : cases ) {
196210 sb .append (" " .repeat (baseIndent + INDENT ));
197211 writeCase (caze , sb , baseIndent , canLiftReturn );
198212 sb .append ("\n " );
199213 }
200214 sb .append (" " .repeat (baseIndent )).append ("}" );
215+ if (canLiftReturn ) {
216+ sb .append (";" );
217+ }
201218 var edit = JavaTextEdit .replaceTree (topLevelIfStat , sb .toString ());
202219 return JavaQuickFix .newQuickFix (ISSUE_MESSAGE ).addTextEdit (edit ).build ();
203220 }
@@ -267,21 +284,22 @@ private void join(List<? extends Tree> elems, String sep, StringBuilder sb) {
267284 }
268285
269286 private sealed interface Case permits PatternMatchCase , EqualityCase , DefaultCase {
270- String scrutinee ();
287+ IdentifierTree scrutinee ();
271288
272289 StatementTree body ();
273290 }
274291
275- private record PatternMatchCase (String scrutinee , PatternTree pattern , List <ExpressionTree > guards , StatementTree body ) implements Case {
292+ private record PatternMatchCase (IdentifierTree scrutinee , PatternTree pattern , List <ExpressionTree > guards ,
293+ StatementTree body ) implements Case {
276294 }
277295
278- private record EqualityCase (String scrutinee , List <ExpressionTree > constants , StatementTree body ) implements Case {
296+ private record EqualityCase (IdentifierTree scrutinee , List <ExpressionTree > constants , StatementTree body ) implements Case {
279297 }
280298
281299 /**
282300 * For simplicity the default case should have the same scrutinee as the cases before it
283301 */
284- private record DefaultCase (String scrutinee , StatementTree body ) implements Case {
302+ private record DefaultCase (IdentifierTree scrutinee , StatementTree body ) implements Case {
285303 }
286304
287305 private record Pair <A , B >(A a , B b ) {
0 commit comments