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