001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2022 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.coding;
021
022import com.puppycrawl.tools.checkstyle.StatelessCheck;
023import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
027
028/**
029 * <p>
030 * Checks that switch statement has a {@code default} clause.
031 * </p>
032 * <p>
033 * Rationale: It's usually a good idea to introduce a
034 * default case in every switch statement. Even if
035 * the developer is sure that all currently possible
036 * cases are covered, this should be expressed in the
037 * default branch, e.g. by using an assertion. This way
038 * the code is protected against later changes, e.g.
039 * introduction of new types in an enumeration type.
040 * </p>
041 * <p>
042 * This check does not validate any switch expressions. Rationale:
043 * The compiler requires switch expressions to be exhaustive. This means
044 * that all possible inputs must be covered.
045 * </p>
046 * <p>
047 * This check does not validate switch statements that use pattern or null
048 * labels. Rationale: Switch statements that use pattern or null labels are
049 * checked by the compiler for exhaustiveness. This means that all possible
050 * inputs must be covered.
051 * </p>
052 * <p>
053 * See the <a href="https://docs.oracle.com/javase/specs/jls/se17/html/jls-15.html#jls-15.28">
054 *     Java Language Specification</a> for more information about switch statements
055 *     and expressions.
056 * </p>
057 * <p>
058 * To configure the check:
059 * </p>
060 * <pre>
061 * &lt;module name=&quot;MissingSwitchDefault&quot;/&gt;
062 * </pre>
063 * <p>Example of violation:</p>
064 * <pre>
065 * switch (i) {    // violation
066 *  case 1:
067 *    break;
068 *  case 2:
069 *    break;
070 * }
071 * </pre>
072 * <p>Example of correct code:</p>
073 * <pre>
074 * switch (i) {
075 *  case 1:
076 *    break;
077 *  case 2:
078 *    break;
079 *  default: // OK
080 *    break;
081 * }
082 * switch (o) {
083 *     case String s: // type pattern
084 *         System.out.println(s);
085 *         break;
086 *     case Integer i: // type pattern
087 *         System.out.println("Integer");
088 *         break;
089 *     default:    // will not compile without default label, thanks to type pattern label usage
090 *         break;
091 * }
092 * </pre>
093 * <p>Example of correct code which does not require default labels:</p>
094 * <pre>
095 *    sealed interface S permits A, B, C {}
096 *    final class A implements S {}
097 *    final class B implements S {}
098 *    record C(int i) implements S {}  // Implicitly final
099 *
100 *    /**
101 *     * The completeness of a switch statement can be
102 *     * determined by the contents of the permits clause
103 *     * of interface S. No default label or default case
104 *     * label is allowed by the compiler in this situation, so
105 *     * this check does not enforce a default label in such
106 *     * statements.
107 *     *&#47;
108 *    static void showSealedCompleteness(S s) {
109 *        switch (s) {
110 *            case A a: System.out.println("A"); break;
111 *            case B b: System.out.println("B"); break;
112 *            case C c: System.out.println("C"); break;
113 *        }
114 *    }
115 *
116 *    /**
117 *     * A total type pattern matches all possible inputs,
118 *     * including null. A default label or
119 *     * default case is not allowed by the compiler in this
120 *     * situation. Accordingly, check does not enforce a
121 *     * default label in this case.
122 *     *&#47;
123 *    static void showTotality(String s) {
124 *        switch (s) {
125 *            case Object o: // total type pattern
126 *                System.out.println("o!");
127 *        }
128 *    }
129 *
130 *    enum Color { RED, GREEN, BLUE }
131 *
132 *    static int showSwitchExpressionExhaustiveness(Color color) {
133 *        switch (color) {
134 *            case RED: System.out.println("RED"); break;
135 *            case BLUE: System.out.println("BLUE"); break;
136 *            case GREEN: System.out.println("GREEN"); break;
137 *            // Check will require default label below, compiler
138 *            // does not enforce a switch statement with constants
139 *            // to be complete.
140 *            default: System.out.println("Something else");
141 *        }
142 *
143 *        // Check will not require default label in switch
144 *        // expression below, because code will not compile
145 *        // if all possible values are not handled. If the
146 *        // 'Color' enum is extended, code will fail to compile.
147 *        return switch (color) {
148 *            case RED:
149 *                yield 1;
150 *            case GREEN:
151 *                yield 2;
152 *            case BLUE:
153 *                yield 3;
154 *        };
155 *    }
156 * </pre>
157 * <p>
158 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
159 * </p>
160 * <p>
161 * Violation Message Keys:
162 * </p>
163 * <ul>
164 * <li>
165 * {@code missing.switch.default}
166 * </li>
167 * </ul>
168 *
169 * @since 3.1
170 */
171@StatelessCheck
172public class MissingSwitchDefaultCheck extends AbstractCheck {
173
174    /**
175     * A key is pointing to the warning message text in "messages.properties"
176     * file.
177     */
178    public static final String MSG_KEY = "missing.switch.default";
179
180    @Override
181    public int[] getDefaultTokens() {
182        return getRequiredTokens();
183    }
184
185    @Override
186    public int[] getAcceptableTokens() {
187        return getRequiredTokens();
188    }
189
190    @Override
191    public int[] getRequiredTokens() {
192        return new int[] {TokenTypes.LITERAL_SWITCH};
193    }
194
195    @Override
196    public void visitToken(DetailAST ast) {
197        if (!containsDefaultLabel(ast)
198                && !containsPatternCaseLabelElement(ast)
199                && !containsDefaultCaseLabelElement(ast)
200                && !isSwitchExpression(ast)) {
201            log(ast, MSG_KEY);
202        }
203    }
204
205    /**
206     * Checks if the case group or its sibling contain the 'default' switch.
207     *
208     * @param detailAst first case group to check.
209     * @return true if 'default' switch found.
210     */
211    private static boolean containsDefaultLabel(DetailAST detailAst) {
212        return TokenUtil.findFirstTokenByPredicate(detailAst,
213                ast -> ast.findFirstToken(TokenTypes.LITERAL_DEFAULT) != null
214        ).isPresent();
215    }
216
217    /**
218     * Checks if a switch block contains a case label with a pattern variable definition.
219     * In this situation, the compiler enforces the given switch block to cover
220     * all possible inputs, and we do not need a default label.
221     *
222     * @param detailAst first case group to check.
223     * @return true if switch block contains a pattern case label element
224     */
225    private static boolean containsPatternCaseLabelElement(DetailAST detailAst) {
226        return TokenUtil.findFirstTokenByPredicate(detailAst, ast -> {
227            return ast.getFirstChild() != null
228                    && ast.getFirstChild().findFirstToken(TokenTypes.PATTERN_VARIABLE_DEF) != null;
229        }).isPresent();
230    }
231
232    /**
233     * Checks if a switch block contains a default case label.
234     *
235     * @param detailAst first case group to check.
236     * @return true if switch block contains default case label
237     */
238    private static boolean containsDefaultCaseLabelElement(DetailAST detailAst) {
239        return TokenUtil.findFirstTokenByPredicate(detailAst, ast -> {
240            return ast.getFirstChild() != null
241                    && ast.getFirstChild().findFirstToken(TokenTypes.LITERAL_DEFAULT) != null;
242        }).isPresent();
243    }
244
245    /**
246     * Checks if this LITERAL_SWITCH token is part of a switch expression.
247     *
248     * @param ast the switch statement we are checking
249     * @return true if part of a switch expression
250     */
251    private static boolean isSwitchExpression(DetailAST ast) {
252        return ast.getParent().getType() == TokenTypes.EXPR
253                || ast.getParent().getParent().getType() == TokenTypes.EXPR;
254    }
255}