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 * &lt;module name=&quot;UnnecessarySemicolonAfterTypeMemberDeclaration&quot;/&gt;
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}