001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files 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.CommonUtil; 027import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 028 029/** 030 * <p> 031 * Checks if unnecessary semicolon is used after type member declaration. 032 * </p> 033 * <p> 034 * This check is not applicable to empty statements (unnecessary semicolons inside methods or 035 * init blocks), 036 * <a href="https://checkstyle.org/config_coding.html#EmptyStatement">EmptyStatement</a> 037 * is responsible for it. 038 * </p> 039 * <ul> 040 * <li> 041 * Property {@code tokens} - tokens to check 042 * Type is {@code java.lang.String[]}. 043 * Validation type is {@code tokenSet}. 044 * Default value is: 045 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF"> 046 * CLASS_DEF</a>, 047 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> 048 * INTERFACE_DEF</a>, 049 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF"> 050 * ENUM_DEF</a>, 051 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_DEF"> 052 * ANNOTATION_DEF</a>, 053 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF"> 054 * VARIABLE_DEF</a>, 055 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF"> 056 * ANNOTATION_FIELD_DEF</a>, 057 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STATIC_INIT"> 058 * STATIC_INIT</a>, 059 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT"> 060 * INSTANCE_INIT</a>, 061 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 062 * CTOR_DEF</a>, 063 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 064 * METHOD_DEF</a>, 065 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF"> 066 * ENUM_CONSTANT_DEF</a>, 067 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF"> 068 * COMPACT_CTOR_DEF</a>, 069 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF"> 070 * RECORD_DEF</a>. 071 * </li> 072 * </ul> 073 * <p> 074 * To configure the check: 075 * </p> 076 * <pre> 077 * <module name="UnnecessarySemicolonAfterTypeMemberDeclaration"/> 078 * </pre> 079 * <p> 080 * Results in following: 081 * </p> 082 * <pre> 083 * class A { 084 * ; // violation, standalone semicolon 085 * {}; // violation, extra semicolon after init block 086 * static {}; // violation, extra semicolon after static init block 087 * A(){}; // violation, extra semicolon after constructor definition 088 * void method() {}; // violation, extra semicolon after method definition 089 * int field = 10;; // violation, extra semicolon after field declaration 090 * 091 * { 092 * ; // no violation, it is empty statement inside init block 093 * } 094 * 095 * static { 096 * ; // no violation, it is empty statement inside static init block 097 * } 098 * 099 * void anotherMethod() { 100 * ; // no violation, it is empty statement 101 * if(true); // no violation, it is empty statement 102 * } 103 * } 104 * </pre> 105 * <p> 106 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 107 * </p> 108 * <p> 109 * Violation Message Keys: 110 * </p> 111 * <ul> 112 * <li> 113 * {@code unnecessary.semicolon} 114 * </li> 115 * </ul> 116 * 117 * @since 8.24 118 */ 119@StatelessCheck 120public final class UnnecessarySemicolonAfterTypeMemberDeclarationCheck extends AbstractCheck { 121 122 /** 123 * A key is pointing to the warning message text in "messages.properties" 124 * file. 125 */ 126 public static final String MSG_SEMI = "unnecessary.semicolon"; 127 128 @Override 129 public int[] getDefaultTokens() { 130 return getAcceptableTokens(); 131 } 132 133 @Override 134 public int[] getAcceptableTokens() { 135 return new int[] { 136 TokenTypes.CLASS_DEF, 137 TokenTypes.INTERFACE_DEF, 138 TokenTypes.ENUM_DEF, 139 TokenTypes.ANNOTATION_DEF, 140 TokenTypes.VARIABLE_DEF, 141 TokenTypes.ANNOTATION_FIELD_DEF, 142 TokenTypes.STATIC_INIT, 143 TokenTypes.INSTANCE_INIT, 144 TokenTypes.CTOR_DEF, 145 TokenTypes.METHOD_DEF, 146 TokenTypes.ENUM_CONSTANT_DEF, 147 TokenTypes.COMPACT_CTOR_DEF, 148 TokenTypes.RECORD_DEF, 149 }; 150 } 151 152 @Override 153 public int[] getRequiredTokens() { 154 return CommonUtil.EMPTY_INT_ARRAY; 155 } 156 157 @Override 158 public void visitToken(DetailAST ast) { 159 switch (ast.getType()) { 160 case TokenTypes.CLASS_DEF: 161 case TokenTypes.INTERFACE_DEF: 162 case TokenTypes.ENUM_DEF: 163 case TokenTypes.ANNOTATION_DEF: 164 case TokenTypes.RECORD_DEF: 165 checkTypeDefinition(ast); 166 break; 167 case TokenTypes.VARIABLE_DEF: 168 checkVariableDefinition(ast); 169 break; 170 case TokenTypes.ENUM_CONSTANT_DEF: 171 checkEnumConstant(ast); 172 break; 173 default: 174 checkTypeMember(ast); 175 break; 176 } 177 } 178 179 /** 180 * Checks if type member has unnecessary semicolon. 181 * 182 * @param ast type member 183 */ 184 private void checkTypeMember(DetailAST ast) { 185 if (isSemicolon(ast.getNextSibling())) { 186 log(ast.getNextSibling(), MSG_SEMI); 187 } 188 } 189 190 /** 191 * Checks if type definition has unnecessary semicolon. 192 * 193 * @param ast type definition 194 */ 195 private void checkTypeDefinition(DetailAST ast) { 196 if (!ScopeUtil.isOuterMostType(ast) && isSemicolon(ast.getNextSibling())) { 197 log(ast.getNextSibling(), MSG_SEMI); 198 } 199 final DetailAST firstMember = 200 ast.findFirstToken(TokenTypes.OBJBLOCK).getFirstChild().getNextSibling(); 201 if (isSemicolon(firstMember) && !ScopeUtil.isInEnumBlock(firstMember)) { 202 log(firstMember, MSG_SEMI); 203 } 204 } 205 206 /** 207 * Checks if variable definition has unnecessary semicolon. 208 * 209 * @param variableDef variable definition 210 */ 211 private void checkVariableDefinition(DetailAST variableDef) { 212 if (isSemicolon(variableDef.getLastChild()) && isSemicolon(variableDef.getNextSibling())) { 213 log(variableDef.getNextSibling(), MSG_SEMI); 214 } 215 } 216 217 /** 218 * Checks if enum constant has unnecessary semicolon. 219 * 220 * @param ast enum constant 221 */ 222 private void checkEnumConstant(DetailAST ast) { 223 final DetailAST next = ast.getNextSibling(); 224 if (isSemicolon(next) && isSemicolon(next.getNextSibling())) { 225 log(next.getNextSibling(), MSG_SEMI); 226 } 227 } 228 229 /** 230 * Checks that {@code ast} is a semicolon. 231 * 232 * @param ast token to check 233 * @return true if ast is semicolon, false otherwise 234 */ 235 private static boolean isSemicolon(DetailAST ast) { 236 return ast.getType() == TokenTypes.SEMI; 237 } 238}