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 /**
63 * Creates a new {@code UseEnhancedSwitchCheck} instance.
64 */
65 public UseEnhancedSwitchCheck() {
66 // no code by default
67 }
68
69 @Override
70 public int[] getDefaultTokens() {
71 return getRequiredTokens();
72 }
73
74 @Override
75 public int[] getAcceptableTokens() {
76 return getRequiredTokens();
77 }
78
79 @Override
80 public int[] getRequiredTokens() {
81 return new int[] {TokenTypes.LITERAL_SWITCH};
82 }
83
84 @Override
85 public void visitToken(DetailAST ast) {
86 final List<DetailAST> caseGroups = getCaseGroups(ast);
87 if (!caseGroups.isEmpty() && allCaseGroupsTerminate(caseGroups)) {
88 log(ast, MSG_KEY);
89 }
90 }
91
92 /**
93 * Check if all case groups terminate (i.e. do not fall through), except the last one which
94 * terminates regardless of its content.
95 *
96 * @param caseGroups the list of case groups to check
97 * @return {@code true} if all case groups terminate, {@code false} otherwise
98 */
99 private static boolean allCaseGroupsTerminate(List<DetailAST> caseGroups) {
100 boolean allCaseGroupsTerminate = true;
101 for (int index = 0; index < caseGroups.size() - 1; index++) {
102 final DetailAST statementList = caseGroups.get(index).findFirstToken(TokenTypes.SLIST);
103
104 if (!CheckUtil.isTerminated(statementList)) {
105 allCaseGroupsTerminate = false;
106 break;
107 }
108 }
109 return allCaseGroupsTerminate;
110 }
111
112 /**
113 * Get all case groups from the switch.
114 *
115 * @param switchAst the AST node representing a {@code LITERAL_SWITCH}
116 * @return all {@code CASE_GROUP} nodes within the switch
117 */
118 private static List<DetailAST> getCaseGroups(DetailAST switchAst) {
119 final List<DetailAST> caseGroups = new ArrayList<>();
120 DetailAST ast = switchAst.getFirstChild();
121 while (ast != null) {
122 if (ast.getType() == TokenTypes.CASE_GROUP) {
123 caseGroups.add(ast);
124 }
125 ast = ast.getNextSibling();
126 }
127 return Collections.unmodifiableList(caseGroups);
128 }
129
130 }