001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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; 026 027/** 028 * <p> 029 * Checks that switch statement has a {@code default} clause. 030 * </p> 031 * <p> 032 * Rationale: It's usually a good idea to introduce a 033 * default case in every switch statement. Even if 034 * the developer is sure that all currently possible 035 * cases are covered, this should be expressed in the 036 * default branch, e.g. by using an assertion. This way 037 * the code is protected against later changes, e.g. 038 * introduction of new types in an enumeration type. Note that 039 * the compiler requires switch expressions to be exhaustive, 040 * so this check does not enforce default branches on 041 * such expressions. 042 * </p> 043 * <p> 044 * To configure the check: 045 * </p> 046 * <pre> 047 * <module name="MissingSwitchDefault"/> 048 * </pre> 049 * <p>Example of violation:</p> 050 * <pre> 051 * switch (i) { // violation 052 * case 1: 053 * break; 054 * case 2: 055 * break; 056 * } 057 * </pre> 058 * <p>Example of correct code:</p> 059 * <pre> 060 * switch (i) { 061 * case 1: 062 * break; 063 * case 2: 064 * break; 065 * default: // OK 066 * break; 067 * } 068 * </pre> 069 * <p> 070 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 071 * </p> 072 * <p> 073 * Violation Message Keys: 074 * </p> 075 * <ul> 076 * <li> 077 * {@code missing.switch.default} 078 * </li> 079 * </ul> 080 * 081 * @since 3.1 082 */ 083@StatelessCheck 084public class MissingSwitchDefaultCheck extends AbstractCheck { 085 086 /** 087 * A key is pointing to the warning message text in "messages.properties" 088 * file. 089 */ 090 public static final String MSG_KEY = "missing.switch.default"; 091 092 @Override 093 public int[] getDefaultTokens() { 094 return getRequiredTokens(); 095 } 096 097 @Override 098 public int[] getAcceptableTokens() { 099 return getRequiredTokens(); 100 } 101 102 @Override 103 public int[] getRequiredTokens() { 104 return new int[] {TokenTypes.LITERAL_SWITCH}; 105 } 106 107 @Override 108 public void visitToken(DetailAST ast) { 109 final DetailAST firstSwitchMemberAst = findFirstSwitchMember(ast); 110 111 if (!containsDefaultSwitch(firstSwitchMemberAst) && !isSwitchExpression(ast)) { 112 log(ast, MSG_KEY); 113 } 114 } 115 116 /** 117 * Checks if the case group or its sibling contain the 'default' switch. 118 * 119 * @param caseGroupAst first case group to check. 120 * @return true if 'default' switch found. 121 */ 122 private static boolean containsDefaultSwitch(DetailAST caseGroupAst) { 123 DetailAST nextAst = caseGroupAst; 124 boolean found = false; 125 126 while (nextAst != null) { 127 if (nextAst.findFirstToken(TokenTypes.LITERAL_DEFAULT) != null) { 128 found = true; 129 break; 130 } 131 132 nextAst = nextAst.getNextSibling(); 133 } 134 135 return found; 136 } 137 138 /** 139 * Returns first CASE_GROUP or SWITCH_RULE ast. 140 * 141 * @param parent the switch statement we are checking 142 * @return ast of first switch member. 143 */ 144 private static DetailAST findFirstSwitchMember(DetailAST parent) { 145 DetailAST switchMember = parent.findFirstToken(TokenTypes.CASE_GROUP); 146 if (switchMember == null) { 147 switchMember = parent.findFirstToken(TokenTypes.SWITCH_RULE); 148 } 149 return switchMember; 150 } 151 152 /** 153 * Checks if this LITERAL_SWITCH token is part of a switch expression. 154 * 155 * @param ast the switch statement we are checking 156 * @return true if part of a switch expression 157 */ 158 private static boolean isSwitchExpression(DetailAST ast) { 159 return ast.getParent().getType() == TokenTypes.EXPR; 160 } 161 162}