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.whitespace; 021 022import java.util.BitSet; 023 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.TokenUtil; 028 029/** 030 * <p> 031 * Checks the policy on the padding of parentheses; that is whether a space is required 032 * after a left parenthesis and before a right parenthesis, or such spaces are 033 * forbidden. No check occurs at the right parenthesis after an empty for 034 * iterator, at the left parenthesis before an empty for initialization, or at 035 * the right parenthesis of a try-with-resources resource specification where 036 * the last resource variable has a trailing semicolon. 037 * Use Check <a href="https://checkstyle.org/config_whitespace.html#EmptyForIteratorPad"> 038 * EmptyForIteratorPad</a> to validate empty for iterators and 039 * <a href="https://checkstyle.org/config_whitespace.html#EmptyForInitializerPad"> 040 * EmptyForInitializerPad</a> to validate empty for initializers. 041 * Typecasts are also not checked, as there is 042 * <a href="https://checkstyle.org/config_whitespace.html#TypecastParenPad"> 043 * TypecastParenPad</a> to validate them. 044 * </p> 045 * <ul> 046 * <li> 047 * Property {@code option} - Specify policy on how to pad parentheses. 048 * Type is {@code com.puppycrawl.tools.checkstyle.checks.whitespace.PadOption}. 049 * Default value is {@code nospace}. 050 * </li> 051 * <li> 052 * Property {@code tokens} - tokens to check 053 * Type is {@code java.lang.String[]}. 054 * Validation type is {@code tokenSet}. 055 * Default value is: 056 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION"> 057 * ANNOTATION</a>, 058 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF"> 059 * ANNOTATION_FIELD_DEF</a>, 060 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_CALL"> 061 * CTOR_CALL</a>, 062 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 063 * CTOR_DEF</a>, 064 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DOT"> 065 * DOT</a>, 066 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF"> 067 * ENUM_CONSTANT_DEF</a>, 068 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EXPR"> 069 * EXPR</a>, 070 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH"> 071 * LITERAL_CATCH</a>, 072 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO"> 073 * LITERAL_DO</a>, 074 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR"> 075 * LITERAL_FOR</a>, 076 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF"> 077 * LITERAL_IF</a>, 078 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NEW"> 079 * LITERAL_NEW</a>, 080 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH"> 081 * LITERAL_SWITCH</a>, 082 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED"> 083 * LITERAL_SYNCHRONIZED</a>, 084 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE"> 085 * LITERAL_WHILE</a>, 086 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL"> 087 * METHOD_CALL</a>, 088 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 089 * METHOD_DEF</a>, 090 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#QUESTION"> 091 * QUESTION</a>, 092 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RESOURCE_SPECIFICATION"> 093 * RESOURCE_SPECIFICATION</a>, 094 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SUPER_CTOR_CALL"> 095 * SUPER_CTOR_CALL</a>, 096 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA"> 097 * LAMBDA</a>, 098 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF"> 099 * RECORD_DEF</a>. 100 * </li> 101 * </ul> 102 * <p> 103 * To configure the check: 104 * </p> 105 * <pre> 106 * <module name="ParenPad"/> 107 * </pre> 108 * <p> 109 * Example: 110 * </p> 111 * <pre> 112 * class Foo { 113 * 114 * int n; 115 * 116 * public void fun() { // OK 117 * bar( 1); // violation, space after left parenthesis 118 * } 119 * 120 * public void bar(int k ) { // violation, space before right parenthesis 121 * while (k > 0) { // OK 122 * } 123 * 124 * Test obj = new Test(k); // OK 125 * } 126 * 127 * public void fun2() { // OK 128 * switch( n) { // violation, space after left parenthesis 129 * case 2: 130 * bar(n); // OK 131 * default: 132 * break; 133 * } 134 * } 135 * 136 * } 137 * </pre> 138 * <p> 139 * To configure the check to require spaces for the 140 * parentheses of constructor, method, and super constructor calls: 141 * </p> 142 * <pre> 143 * <module name="ParenPad"> 144 * <property name="tokens" value="LITERAL_FOR, LITERAL_CATCH, 145 * SUPER_CTOR_CALL"/> 146 * <property name="option" value="space"/> 147 * </module> 148 * </pre> 149 * <p> 150 * Example: 151 * </p> 152 * <pre> 153 * class Foo { 154 * 155 * int x; 156 * 157 * public Foo(int n) { 158 * } 159 * 160 * public void fun() { 161 * try { 162 * System.out.println(x); 163 * } catch( IOException e) { // violation, no space before right parenthesis 164 * } catch( Exception e ) { // OK 165 * } 166 * 167 * for ( int i = 0; i < x; i++ ) { // OK 168 * } 169 * } 170 * 171 * } 172 * 173 * class Bar extends Foo { 174 * 175 * public Bar() { 176 * super(1 ); // violation, no space after left parenthesis 177 * } 178 * 179 * public Bar(int k) { 180 * super( k ); // OK 181 * 182 * for ( int i = 0; i < k; i++) { // violation, no space before right parenthesis 183 * } 184 * } 185 * 186 * } 187 * </pre> 188 * <p> 189 * The following cases are not checked: 190 * </p> 191 * <pre> 192 * for ( ; i < j; i++, j--) // no check after left parenthesis 193 * for (Iterator it = xs.iterator(); it.hasNext(); ) // no check before right parenthesis 194 * try (Closeable resource = acquire(); ) // no check before right parenthesis 195 * </pre> 196 * <p> 197 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 198 * </p> 199 * <p> 200 * Violation Message Keys: 201 * </p> 202 * <ul> 203 * <li> 204 * {@code ws.followed} 205 * </li> 206 * <li> 207 * {@code ws.notFollowed} 208 * </li> 209 * <li> 210 * {@code ws.notPreceded} 211 * </li> 212 * <li> 213 * {@code ws.preceded} 214 * </li> 215 * </ul> 216 * 217 * @since 3.0 218 */ 219public class ParenPadCheck extends AbstractParenPadCheck { 220 221 /** 222 * Tokens that this check handles. 223 */ 224 private final BitSet acceptableTokens; 225 226 /** 227 * Initializes acceptableTokens. 228 */ 229 public ParenPadCheck() { 230 acceptableTokens = TokenUtil.asBitSet(makeAcceptableTokens()); 231 } 232 233 @Override 234 public int[] getDefaultTokens() { 235 return makeAcceptableTokens(); 236 } 237 238 @Override 239 public int[] getAcceptableTokens() { 240 return makeAcceptableTokens(); 241 } 242 243 @Override 244 public int[] getRequiredTokens() { 245 return CommonUtil.EMPTY_INT_ARRAY; 246 } 247 248 @Override 249 public void visitToken(DetailAST ast) { 250 switch (ast.getType()) { 251 case TokenTypes.METHOD_CALL: 252 processLeft(ast); 253 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 254 break; 255 case TokenTypes.DOT: 256 case TokenTypes.EXPR: 257 case TokenTypes.QUESTION: 258 processExpression(ast); 259 break; 260 case TokenTypes.LITERAL_FOR: 261 visitLiteralFor(ast); 262 break; 263 case TokenTypes.ANNOTATION: 264 case TokenTypes.ENUM_CONSTANT_DEF: 265 case TokenTypes.LITERAL_NEW: 266 case TokenTypes.LITERAL_SYNCHRONIZED: 267 case TokenTypes.LAMBDA: 268 visitTokenWithOptionalParentheses(ast); 269 break; 270 case TokenTypes.RESOURCE_SPECIFICATION: 271 visitResourceSpecification(ast); 272 break; 273 default: 274 processLeft(ast.findFirstToken(TokenTypes.LPAREN)); 275 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 276 } 277 } 278 279 /** 280 * Checks parens in token which may not contain parens, e.g. 281 * {@link TokenTypes#ENUM_CONSTANT_DEF}, {@link TokenTypes#ANNOTATION} 282 * {@link TokenTypes#LITERAL_SYNCHRONIZED}, {@link TokenTypes#LITERAL_NEW} and 283 * {@link TokenTypes#LAMBDA}. 284 * 285 * @param ast the token to check. 286 */ 287 private void visitTokenWithOptionalParentheses(DetailAST ast) { 288 final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN); 289 if (parenAst != null) { 290 processLeft(parenAst); 291 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 292 } 293 } 294 295 /** 296 * Checks parens in {@link TokenTypes#RESOURCE_SPECIFICATION}. 297 * 298 * @param ast the token to check. 299 */ 300 private void visitResourceSpecification(DetailAST ast) { 301 processLeft(ast.findFirstToken(TokenTypes.LPAREN)); 302 final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN); 303 if (!hasPrecedingSemiColon(rparen)) { 304 processRight(rparen); 305 } 306 } 307 308 /** 309 * Checks that a token is preceded by a semicolon. 310 * 311 * @param ast the token to check 312 * @return whether a token is preceded by a semicolon 313 */ 314 private static boolean hasPrecedingSemiColon(DetailAST ast) { 315 return ast.getPreviousSibling().getType() == TokenTypes.SEMI; 316 } 317 318 /** 319 * Checks parens in {@link TokenTypes#LITERAL_FOR}. 320 * 321 * @param ast the token to check. 322 */ 323 private void visitLiteralFor(DetailAST ast) { 324 final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN); 325 if (!isPrecedingEmptyForInit(lparen)) { 326 processLeft(lparen); 327 } 328 final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN); 329 if (!isFollowsEmptyForIterator(rparen)) { 330 processRight(rparen); 331 } 332 } 333 334 /** 335 * Checks parens inside {@link TokenTypes#EXPR}, {@link TokenTypes#QUESTION} 336 * and {@link TokenTypes#METHOD_CALL}. 337 * 338 * @param ast the token to check. 339 */ 340 private void processExpression(DetailAST ast) { 341 DetailAST currentNode = ast.getFirstChild(); 342 while (currentNode != null) { 343 if (currentNode.getType() == TokenTypes.LPAREN) { 344 processLeft(currentNode); 345 } 346 else if (currentNode.getType() == TokenTypes.RPAREN && !isInTypecast(currentNode)) { 347 processRight(currentNode); 348 } 349 else if (currentNode.hasChildren() && !isAcceptableToken(currentNode)) { 350 // Traverse all subtree tokens which will never be configured 351 // to be launched in visitToken() 352 currentNode = currentNode.getFirstChild(); 353 continue; 354 } 355 356 // Go up after processing the last child 357 while (currentNode.getNextSibling() == null && currentNode.getParent() != ast) { 358 currentNode = currentNode.getParent(); 359 } 360 currentNode = currentNode.getNextSibling(); 361 } 362 } 363 364 /** 365 * Checks whether AcceptableTokens contains the given ast. 366 * 367 * @param ast the token to check. 368 * @return true if the ast is in AcceptableTokens. 369 */ 370 private boolean isAcceptableToken(DetailAST ast) { 371 return acceptableTokens.get(ast.getType()); 372 } 373 374 /** 375 * Returns array of acceptable tokens. 376 * 377 * @return acceptableTokens. 378 */ 379 private static int[] makeAcceptableTokens() { 380 return new int[] {TokenTypes.ANNOTATION, 381 TokenTypes.ANNOTATION_FIELD_DEF, 382 TokenTypes.CTOR_CALL, 383 TokenTypes.CTOR_DEF, 384 TokenTypes.DOT, 385 TokenTypes.ENUM_CONSTANT_DEF, 386 TokenTypes.EXPR, 387 TokenTypes.LITERAL_CATCH, 388 TokenTypes.LITERAL_DO, 389 TokenTypes.LITERAL_FOR, 390 TokenTypes.LITERAL_IF, 391 TokenTypes.LITERAL_NEW, 392 TokenTypes.LITERAL_SWITCH, 393 TokenTypes.LITERAL_SYNCHRONIZED, 394 TokenTypes.LITERAL_WHILE, 395 TokenTypes.METHOD_CALL, 396 TokenTypes.METHOD_DEF, 397 TokenTypes.QUESTION, 398 TokenTypes.RESOURCE_SPECIFICATION, 399 TokenTypes.SUPER_CTOR_CALL, 400 TokenTypes.LAMBDA, 401 TokenTypes.RECORD_DEF, 402 }; 403 } 404 405 /** 406 * Checks whether {@link TokenTypes#RPAREN} is a closing paren 407 * of a {@link TokenTypes#TYPECAST}. 408 * 409 * @param ast of a {@link TokenTypes#RPAREN} to check. 410 * @return true if ast is a closing paren of a {@link TokenTypes#TYPECAST}. 411 */ 412 private static boolean isInTypecast(DetailAST ast) { 413 boolean result = false; 414 if (ast.getParent().getType() == TokenTypes.TYPECAST) { 415 final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN); 416 if (TokenUtil.areOnSameLine(firstRparen, ast) 417 && firstRparen.getColumnNo() == ast.getColumnNo()) { 418 result = true; 419 } 420 } 421 return result; 422 } 423 424 /** 425 * Checks that a token follows an empty for iterator. 426 * 427 * @param ast the token to check 428 * @return whether a token follows an empty for iterator 429 */ 430 private static boolean isFollowsEmptyForIterator(DetailAST ast) { 431 boolean result = false; 432 final DetailAST parent = ast.getParent(); 433 // Only traditional for statements are examined, not for-each statements 434 if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) { 435 final DetailAST forIterator = 436 parent.findFirstToken(TokenTypes.FOR_ITERATOR); 437 result = !forIterator.hasChildren(); 438 } 439 return result; 440 } 441 442 /** 443 * Checks that a token precedes an empty for initializer. 444 * 445 * @param ast the token to check 446 * @return whether a token precedes an empty for initializer 447 */ 448 private static boolean isPrecedingEmptyForInit(DetailAST ast) { 449 boolean result = false; 450 final DetailAST parent = ast.getParent(); 451 // Only traditional for statements are examined, not for-each statements 452 if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) { 453 final DetailAST forIterator = 454 parent.findFirstToken(TokenTypes.FOR_INIT); 455 result = !forIterator.hasChildren(); 456 } 457 return result; 458 } 459 460}