1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2026 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.checks.coding;
21
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25
26 import com.puppycrawl.tools.checkstyle.StatelessCheck;
27 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28 import com.puppycrawl.tools.checkstyle.api.DetailAST;
29 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
30 import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
31
32 /**
33 * <div>
34 * Ensures that the enhanced switch (using {@code ->} for case labels) is used
35 * instead of the traditional switch (using {@code :} for case labels) where possible.
36 * </div>
37 *
38 * <p>
39 * Rationale: Java 14 has introduced enhancements for switch statements and expressions
40 * that disallow fall-through behavior. The enhanced switch syntax using {@code ->}
41 * for case labels typically leads to more concise and readable code, reducing the likelihood
42 * of errors associated with fall-through cases.
43 * </p>
44 *
45 * <p>
46 * See the <a href="https://docs.oracle.com/javase/specs/jls/se22/html/jls-14.html#jls-14.11.1-200">
47 * Java Language Specification</a> for more information about {@code ->} case labels, also known as
48 * "switch rules".
49 * </p>
50 *
51 * @since 13.3.0
52 */
53 @StatelessCheck
54 public class UseEnhancedSwitchCheck extends AbstractCheck {
55
56 /**
57 * A key is pointing to the warning message text in "messages.properties"
58 * file.
59 */
60 public static final String MSG_KEY = "use.enhanced.switch";
61
62 @Override
63 public int[] getDefaultTokens() {
64 return getRequiredTokens();
65 }
66
67 @Override
68 public int[] getAcceptableTokens() {
69 return getRequiredTokens();
70 }
71
72 @Override
73 public int[] getRequiredTokens() {
74 return new int[] {TokenTypes.LITERAL_SWITCH};
75 }
76
77 @Override
78 public void visitToken(DetailAST ast) {
79 final List<DetailAST> caseGroups = getCaseGroups(ast);
80 if (!caseGroups.isEmpty() && allCaseGroupsTerminate(caseGroups)) {
81 log(ast, MSG_KEY);
82 }
83 }
84
85 /**
86 * Check if all case groups terminate (i.e. do not fall through), except the last one which
87 * terminates regardless of its content.
88 *
89 * @param caseGroups the list of case groups to check
90 * @return {@code true} if all case groups terminate, {@code false} otherwise
91 */
92 private static boolean allCaseGroupsTerminate(List<DetailAST> caseGroups) {
93 boolean allCaseGroupsTerminate = true;
94 for (int index = 0; index < caseGroups.size() - 1; index++) {
95 final DetailAST statementList = caseGroups.get(index).findFirstToken(TokenTypes.SLIST);
96
97 if (!CheckUtil.isTerminated(statementList)) {
98 allCaseGroupsTerminate = false;
99 break;
100 }
101 }
102 return allCaseGroupsTerminate;
103 }
104
105 /**
106 * Get all case groups from the switch.
107 *
108 * @param switchAst the AST node representing a {@code LITERAL_SWITCH}
109 * @return all {@code CASE_GROUP} nodes within the switch
110 */
111 private static List<DetailAST> getCaseGroups(DetailAST switchAst) {
112 final List<DetailAST> caseGroups = new ArrayList<>();
113 DetailAST ast = switchAst.getFirstChild();
114 while (ast != null) {
115 if (ast.getType() == TokenTypes.CASE_GROUP) {
116 caseGroups.add(ast);
117 }
118 ast = ast.getNextSibling();
119 }
120 return Collections.unmodifiableList(caseGroups);
121 }
122
123 }