001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2025 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 * <div> 031 * Checks if unnecessary semicolon is used after type member declaration. 032 * </div> 033 * 034 * <p> 035 * Notes: 036 * This check is not applicable to empty statements (unnecessary semicolons inside methods or 037 * init blocks), 038 * <a href="https://checkstyle.org/checks/coding/emptystatement.html">EmptyStatement</a> 039 * is responsible for it. 040 * </p> 041 * 042 * @since 8.24 043 */ 044@StatelessCheck 045public final class UnnecessarySemicolonAfterTypeMemberDeclarationCheck extends AbstractCheck { 046 047 /** 048 * A key is pointing to the warning message text in "messages.properties" 049 * file. 050 */ 051 public static final String MSG_SEMI = "unnecessary.semicolon"; 052 053 @Override 054 public int[] getDefaultTokens() { 055 return getAcceptableTokens(); 056 } 057 058 @Override 059 public int[] getAcceptableTokens() { 060 return new int[] { 061 TokenTypes.CLASS_DEF, 062 TokenTypes.INTERFACE_DEF, 063 TokenTypes.ENUM_DEF, 064 TokenTypes.ANNOTATION_DEF, 065 TokenTypes.VARIABLE_DEF, 066 TokenTypes.ANNOTATION_FIELD_DEF, 067 TokenTypes.STATIC_INIT, 068 TokenTypes.INSTANCE_INIT, 069 TokenTypes.CTOR_DEF, 070 TokenTypes.METHOD_DEF, 071 TokenTypes.ENUM_CONSTANT_DEF, 072 TokenTypes.COMPACT_CTOR_DEF, 073 TokenTypes.RECORD_DEF, 074 }; 075 } 076 077 @Override 078 public int[] getRequiredTokens() { 079 return CommonUtil.EMPTY_INT_ARRAY; 080 } 081 082 @Override 083 public void visitToken(DetailAST ast) { 084 switch (ast.getType()) { 085 case TokenTypes.CLASS_DEF, 086 TokenTypes.INTERFACE_DEF, 087 TokenTypes.ENUM_DEF, 088 TokenTypes.ANNOTATION_DEF, 089 TokenTypes.RECORD_DEF -> checkTypeDefinition(ast); 090 case TokenTypes.VARIABLE_DEF -> checkVariableDefinition(ast); 091 case TokenTypes.ENUM_CONSTANT_DEF -> checkEnumConstant(ast); 092 default -> checkTypeMember(ast); 093 } 094 } 095 096 /** 097 * Checks if type member has unnecessary semicolon. 098 * 099 * @param ast type member 100 */ 101 private void checkTypeMember(DetailAST ast) { 102 if (isSemicolon(ast.getNextSibling())) { 103 log(ast.getNextSibling(), MSG_SEMI); 104 } 105 } 106 107 /** 108 * Checks if type definition has unnecessary semicolon. 109 * 110 * @param ast type definition 111 */ 112 private void checkTypeDefinition(DetailAST ast) { 113 if (!ScopeUtil.isOuterMostType(ast) && isSemicolon(ast.getNextSibling())) { 114 log(ast.getNextSibling(), MSG_SEMI); 115 } 116 final DetailAST firstMember = 117 ast.findFirstToken(TokenTypes.OBJBLOCK).getFirstChild().getNextSibling(); 118 if (isSemicolon(firstMember) && !ScopeUtil.isInEnumBlock(firstMember)) { 119 log(firstMember, MSG_SEMI); 120 } 121 } 122 123 /** 124 * Checks if variable definition has unnecessary semicolon. 125 * 126 * @param variableDef variable definition 127 */ 128 private void checkVariableDefinition(DetailAST variableDef) { 129 if (isSemicolon(variableDef.getLastChild()) && isSemicolon(variableDef.getNextSibling())) { 130 log(variableDef.getNextSibling(), MSG_SEMI); 131 } 132 } 133 134 /** 135 * Checks if enum constant has unnecessary semicolon. 136 * 137 * @param ast enum constant 138 */ 139 private void checkEnumConstant(DetailAST ast) { 140 final DetailAST next = ast.getNextSibling(); 141 if (isSemicolon(next) && isSemicolon(next.getNextSibling())) { 142 log(next.getNextSibling(), MSG_SEMI); 143 } 144 } 145 146 /** 147 * Checks that {@code ast} is a semicolon. 148 * 149 * @param ast token to check 150 * @return true if ast is semicolon, false otherwise 151 */ 152 private static boolean isSemicolon(DetailAST ast) { 153 return ast != null && ast.getType() == TokenTypes.SEMI; 154 } 155}