001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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/checks/coding/emptystatement.html">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 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 075 * </p> 076 * <p> 077 * Violation Message Keys: 078 * </p> 079 * <ul> 080 * <li> 081 * {@code unnecessary.semicolon} 082 * </li> 083 * </ul> 084 * 085 * @since 8.24 086 */ 087@StatelessCheck 088public final class UnnecessarySemicolonAfterTypeMemberDeclarationCheck extends AbstractCheck { 089 090 /** 091 * A key is pointing to the warning message text in "messages.properties" 092 * file. 093 */ 094 public static final String MSG_SEMI = "unnecessary.semicolon"; 095 096 @Override 097 public int[] getDefaultTokens() { 098 return getAcceptableTokens(); 099 } 100 101 @Override 102 public int[] getAcceptableTokens() { 103 return new int[] { 104 TokenTypes.CLASS_DEF, 105 TokenTypes.INTERFACE_DEF, 106 TokenTypes.ENUM_DEF, 107 TokenTypes.ANNOTATION_DEF, 108 TokenTypes.VARIABLE_DEF, 109 TokenTypes.ANNOTATION_FIELD_DEF, 110 TokenTypes.STATIC_INIT, 111 TokenTypes.INSTANCE_INIT, 112 TokenTypes.CTOR_DEF, 113 TokenTypes.METHOD_DEF, 114 TokenTypes.ENUM_CONSTANT_DEF, 115 TokenTypes.COMPACT_CTOR_DEF, 116 TokenTypes.RECORD_DEF, 117 }; 118 } 119 120 @Override 121 public int[] getRequiredTokens() { 122 return CommonUtil.EMPTY_INT_ARRAY; 123 } 124 125 @Override 126 public void visitToken(DetailAST ast) { 127 switch (ast.getType()) { 128 case TokenTypes.CLASS_DEF: 129 case TokenTypes.INTERFACE_DEF: 130 case TokenTypes.ENUM_DEF: 131 case TokenTypes.ANNOTATION_DEF: 132 case TokenTypes.RECORD_DEF: 133 checkTypeDefinition(ast); 134 break; 135 case TokenTypes.VARIABLE_DEF: 136 checkVariableDefinition(ast); 137 break; 138 case TokenTypes.ENUM_CONSTANT_DEF: 139 checkEnumConstant(ast); 140 break; 141 default: 142 checkTypeMember(ast); 143 break; 144 } 145 } 146 147 /** 148 * Checks if type member has unnecessary semicolon. 149 * 150 * @param ast type member 151 */ 152 private void checkTypeMember(DetailAST ast) { 153 if (isSemicolon(ast.getNextSibling())) { 154 log(ast.getNextSibling(), MSG_SEMI); 155 } 156 } 157 158 /** 159 * Checks if type definition has unnecessary semicolon. 160 * 161 * @param ast type definition 162 */ 163 private void checkTypeDefinition(DetailAST ast) { 164 if (!ScopeUtil.isOuterMostType(ast) && isSemicolon(ast.getNextSibling())) { 165 log(ast.getNextSibling(), MSG_SEMI); 166 } 167 final DetailAST firstMember = 168 ast.findFirstToken(TokenTypes.OBJBLOCK).getFirstChild().getNextSibling(); 169 if (isSemicolon(firstMember) && !ScopeUtil.isInEnumBlock(firstMember)) { 170 log(firstMember, MSG_SEMI); 171 } 172 } 173 174 /** 175 * Checks if variable definition has unnecessary semicolon. 176 * 177 * @param variableDef variable definition 178 */ 179 private void checkVariableDefinition(DetailAST variableDef) { 180 if (isSemicolon(variableDef.getLastChild()) && isSemicolon(variableDef.getNextSibling())) { 181 log(variableDef.getNextSibling(), MSG_SEMI); 182 } 183 } 184 185 /** 186 * Checks if enum constant has unnecessary semicolon. 187 * 188 * @param ast enum constant 189 */ 190 private void checkEnumConstant(DetailAST ast) { 191 final DetailAST next = ast.getNextSibling(); 192 if (isSemicolon(next) && isSemicolon(next.getNextSibling())) { 193 log(next.getNextSibling(), MSG_SEMI); 194 } 195 } 196 197 /** 198 * Checks that {@code ast} is a semicolon. 199 * 200 * @param ast token to check 201 * @return true if ast is semicolon, false otherwise 202 */ 203 private static boolean isSemicolon(DetailAST ast) { 204 return ast != null && ast.getType() == TokenTypes.SEMI; 205 } 206}