View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import com.puppycrawl.tools.checkstyle.StatelessCheck;
23  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24  import com.puppycrawl.tools.checkstyle.api.DetailAST;
25  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
27  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
28  
29  /**
30   * <div>
31   * Checks if unnecessary semicolon is used after type member declaration.
32   * </div>
33   *
34   * <p>
35   * Notes:
36   * This check is not applicable to empty statements (unnecessary semicolons inside methods or
37   * init blocks),
38   * <a href="https://checkstyle.org/checks/coding/emptystatement.html">EmptyStatement</a>
39   * is responsible for it.
40   * </p>
41   * <ul>
42   * <li>
43   * Property {@code tokens} - tokens to check
44   * Type is {@code java.lang.String[]}.
45   * Validation type is {@code tokenSet}.
46   * Default value is:
47   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
48   * CLASS_DEF</a>,
49   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
50   * INTERFACE_DEF</a>,
51   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
52   * ENUM_DEF</a>,
53   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_DEF">
54   * ANNOTATION_DEF</a>,
55   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
56   * VARIABLE_DEF</a>,
57   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">
58   * ANNOTATION_FIELD_DEF</a>,
59   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STATIC_INIT">
60   * STATIC_INIT</a>,
61   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT">
62   * INSTANCE_INIT</a>,
63   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
64   * CTOR_DEF</a>,
65   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
66   * METHOD_DEF</a>,
67   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF">
68   * ENUM_CONSTANT_DEF</a>,
69   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF">
70   * COMPACT_CTOR_DEF</a>,
71   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF">
72   * RECORD_DEF</a>.
73   * </li>
74   * </ul>
75   *
76   * <p>
77   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
78   * </p>
79   *
80   * <p>
81   * Violation Message Keys:
82   * </p>
83   * <ul>
84   * <li>
85   * {@code unnecessary.semicolon}
86   * </li>
87   * </ul>
88   *
89   * @since 8.24
90   */
91  @StatelessCheck
92  public final class UnnecessarySemicolonAfterTypeMemberDeclarationCheck extends AbstractCheck {
93  
94      /**
95       * A key is pointing to the warning message text in "messages.properties"
96       * file.
97       */
98      public static final String MSG_SEMI = "unnecessary.semicolon";
99  
100     @Override
101     public int[] getDefaultTokens() {
102         return getAcceptableTokens();
103     }
104 
105     @Override
106     public int[] getAcceptableTokens() {
107         return new int[] {
108             TokenTypes.CLASS_DEF,
109             TokenTypes.INTERFACE_DEF,
110             TokenTypes.ENUM_DEF,
111             TokenTypes.ANNOTATION_DEF,
112             TokenTypes.VARIABLE_DEF,
113             TokenTypes.ANNOTATION_FIELD_DEF,
114             TokenTypes.STATIC_INIT,
115             TokenTypes.INSTANCE_INIT,
116             TokenTypes.CTOR_DEF,
117             TokenTypes.METHOD_DEF,
118             TokenTypes.ENUM_CONSTANT_DEF,
119             TokenTypes.COMPACT_CTOR_DEF,
120             TokenTypes.RECORD_DEF,
121         };
122     }
123 
124     @Override
125     public int[] getRequiredTokens() {
126         return CommonUtil.EMPTY_INT_ARRAY;
127     }
128 
129     @Override
130     public void visitToken(DetailAST ast) {
131         switch (ast.getType()) {
132             case TokenTypes.CLASS_DEF:
133             case TokenTypes.INTERFACE_DEF:
134             case TokenTypes.ENUM_DEF:
135             case TokenTypes.ANNOTATION_DEF:
136             case TokenTypes.RECORD_DEF:
137                 checkTypeDefinition(ast);
138                 break;
139             case TokenTypes.VARIABLE_DEF:
140                 checkVariableDefinition(ast);
141                 break;
142             case TokenTypes.ENUM_CONSTANT_DEF:
143                 checkEnumConstant(ast);
144                 break;
145             default:
146                 checkTypeMember(ast);
147                 break;
148         }
149     }
150 
151     /**
152      * Checks if type member has unnecessary semicolon.
153      *
154      * @param ast type member
155      */
156     private void checkTypeMember(DetailAST ast) {
157         if (isSemicolon(ast.getNextSibling())) {
158             log(ast.getNextSibling(), MSG_SEMI);
159         }
160     }
161 
162     /**
163      * Checks if type definition has unnecessary semicolon.
164      *
165      * @param ast type definition
166      */
167     private void checkTypeDefinition(DetailAST ast) {
168         if (!ScopeUtil.isOuterMostType(ast) && isSemicolon(ast.getNextSibling())) {
169             log(ast.getNextSibling(), MSG_SEMI);
170         }
171         final DetailAST firstMember =
172             ast.findFirstToken(TokenTypes.OBJBLOCK).getFirstChild().getNextSibling();
173         if (isSemicolon(firstMember) && !ScopeUtil.isInEnumBlock(firstMember)) {
174             log(firstMember, MSG_SEMI);
175         }
176     }
177 
178     /**
179      * Checks if variable definition has unnecessary semicolon.
180      *
181      * @param variableDef variable definition
182      */
183     private void checkVariableDefinition(DetailAST variableDef) {
184         if (isSemicolon(variableDef.getLastChild()) && isSemicolon(variableDef.getNextSibling())) {
185             log(variableDef.getNextSibling(), MSG_SEMI);
186         }
187     }
188 
189     /**
190      * Checks if enum constant has unnecessary semicolon.
191      *
192      * @param ast enum constant
193      */
194     private void checkEnumConstant(DetailAST ast) {
195         final DetailAST next = ast.getNextSibling();
196         if (isSemicolon(next) && isSemicolon(next.getNextSibling())) {
197             log(next.getNextSibling(), MSG_SEMI);
198         }
199     }
200 
201     /**
202      * Checks that {@code ast} is a semicolon.
203      *
204      * @param ast token to check
205      * @return true if ast is semicolon, false otherwise
206      */
207     private static boolean isSemicolon(DetailAST ast) {
208         return ast != null && ast.getType() == TokenTypes.SEMI;
209     }
210 }