001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code 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.Arrays; 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 semi-colon. 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 * The array of Acceptable Tokens. 223 */ 224 private final int[] acceptableTokens; 225 226 /** 227 * Initializes and sorts acceptableTokens to make binary search over it possible. 228 */ 229 public ParenPadCheck() { 230 acceptableTokens = makeAcceptableTokens(); 231 Arrays.sort(acceptableTokens); 232 } 233 234 @Override 235 public int[] getDefaultTokens() { 236 return makeAcceptableTokens(); 237 } 238 239 @Override 240 public int[] getAcceptableTokens() { 241 return makeAcceptableTokens(); 242 } 243 244 @Override 245 public int[] getRequiredTokens() { 246 return CommonUtil.EMPTY_INT_ARRAY; 247 } 248 249 @Override 250 public void visitToken(DetailAST ast) { 251 switch (ast.getType()) { 252 case TokenTypes.METHOD_CALL: 253 processLeft(ast); 254 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 255 break; 256 case TokenTypes.DOT: 257 case TokenTypes.EXPR: 258 case TokenTypes.QUESTION: 259 processExpression(ast); 260 break; 261 case TokenTypes.LITERAL_FOR: 262 visitLiteralFor(ast); 263 break; 264 case TokenTypes.ANNOTATION: 265 case TokenTypes.ENUM_CONSTANT_DEF: 266 case TokenTypes.LITERAL_NEW: 267 case TokenTypes.LITERAL_SYNCHRONIZED: 268 case TokenTypes.LAMBDA: 269 visitTokenWithOptionalParentheses(ast); 270 break; 271 case TokenTypes.RESOURCE_SPECIFICATION: 272 visitResourceSpecification(ast); 273 break; 274 default: 275 processLeft(ast.findFirstToken(TokenTypes.LPAREN)); 276 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 277 } 278 } 279 280 /** 281 * Checks parens in token which may not contain parens, e.g. 282 * {@link TokenTypes#ENUM_CONSTANT_DEF}, {@link TokenTypes#ANNOTATION} 283 * {@link TokenTypes#LITERAL_SYNCHRONIZED}, {@link TokenTypes#LITERAL_NEW} and 284 * {@link TokenTypes#LAMBDA}. 285 * 286 * @param ast the token to check. 287 */ 288 private void visitTokenWithOptionalParentheses(DetailAST ast) { 289 final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN); 290 if (parenAst != null) { 291 processLeft(parenAst); 292 processRight(ast.findFirstToken(TokenTypes.RPAREN)); 293 } 294 } 295 296 /** 297 * Checks parens in {@link TokenTypes#RESOURCE_SPECIFICATION}. 298 * 299 * @param ast the token to check. 300 */ 301 private void visitResourceSpecification(DetailAST ast) { 302 processLeft(ast.findFirstToken(TokenTypes.LPAREN)); 303 final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN); 304 if (!hasPrecedingSemiColon(rparen)) { 305 processRight(rparen); 306 } 307 } 308 309 /** 310 * Checks that a token is preceded by a semi-colon. 311 * 312 * @param ast the token to check 313 * @return whether a token is preceded by a semi-colon 314 */ 315 private static boolean hasPrecedingSemiColon(DetailAST ast) { 316 return ast.getPreviousSibling().getType() == TokenTypes.SEMI; 317 } 318 319 /** 320 * Checks parens in {@link TokenTypes#LITERAL_FOR}. 321 * 322 * @param ast the token to check. 323 */ 324 private void visitLiteralFor(DetailAST ast) { 325 final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN); 326 if (!isPrecedingEmptyForInit(lparen)) { 327 processLeft(lparen); 328 } 329 final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN); 330 if (!isFollowsEmptyForIterator(rparen)) { 331 processRight(rparen); 332 } 333 } 334 335 /** 336 * Checks parens inside {@link TokenTypes#EXPR}, {@link TokenTypes#QUESTION} 337 * and {@link TokenTypes#METHOD_CALL}. 338 * 339 * @param ast the token to check. 340 */ 341 private void processExpression(DetailAST ast) { 342 DetailAST currentNode = ast.getFirstChild(); 343 while (currentNode != null) { 344 if (currentNode.getType() == TokenTypes.LPAREN) { 345 processLeft(currentNode); 346 } 347 else if (currentNode.getType() == TokenTypes.RPAREN && !isInTypecast(currentNode)) { 348 processRight(currentNode); 349 } 350 else if (currentNode.hasChildren() && !isAcceptableToken(currentNode)) { 351 // Traverse all subtree tokens which will never be configured 352 // to be launched in visitToken() 353 currentNode = currentNode.getFirstChild(); 354 continue; 355 } 356 357 // Go up after processing the last child 358 while (currentNode.getNextSibling() == null && currentNode.getParent() != ast) { 359 currentNode = currentNode.getParent(); 360 } 361 currentNode = currentNode.getNextSibling(); 362 } 363 } 364 365 /** 366 * Checks whether AcceptableTokens contains the given ast. 367 * 368 * @param ast the token to check. 369 * @return true if the ast is in AcceptableTokens. 370 */ 371 private boolean isAcceptableToken(DetailAST ast) { 372 return Arrays.binarySearch(acceptableTokens, ast.getType()) >= 0; 373 } 374 375 /** 376 * Returns array of acceptable tokens. 377 * 378 * @return acceptableTokens. 379 */ 380 private static int[] makeAcceptableTokens() { 381 return new int[] {TokenTypes.ANNOTATION, 382 TokenTypes.ANNOTATION_FIELD_DEF, 383 TokenTypes.CTOR_CALL, 384 TokenTypes.CTOR_DEF, 385 TokenTypes.DOT, 386 TokenTypes.ENUM_CONSTANT_DEF, 387 TokenTypes.EXPR, 388 TokenTypes.LITERAL_CATCH, 389 TokenTypes.LITERAL_DO, 390 TokenTypes.LITERAL_FOR, 391 TokenTypes.LITERAL_IF, 392 TokenTypes.LITERAL_NEW, 393 TokenTypes.LITERAL_SWITCH, 394 TokenTypes.LITERAL_SYNCHRONIZED, 395 TokenTypes.LITERAL_WHILE, 396 TokenTypes.METHOD_CALL, 397 TokenTypes.METHOD_DEF, 398 TokenTypes.QUESTION, 399 TokenTypes.RESOURCE_SPECIFICATION, 400 TokenTypes.SUPER_CTOR_CALL, 401 TokenTypes.LAMBDA, 402 TokenTypes.RECORD_DEF, 403 }; 404 } 405 406 /** 407 * Checks whether {@link TokenTypes#RPAREN} is a closing paren 408 * of a {@link TokenTypes#TYPECAST}. 409 * 410 * @param ast of a {@link TokenTypes#RPAREN} to check. 411 * @return true if ast is a closing paren of a {@link TokenTypes#TYPECAST}. 412 */ 413 private static boolean isInTypecast(DetailAST ast) { 414 boolean result = false; 415 if (ast.getParent().getType() == TokenTypes.TYPECAST) { 416 final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN); 417 if (TokenUtil.areOnSameLine(firstRparen, ast) 418 && firstRparen.getColumnNo() == ast.getColumnNo()) { 419 result = true; 420 } 421 } 422 return result; 423 } 424 425 /** 426 * Checks that a token follows an empty for iterator. 427 * 428 * @param ast the token to check 429 * @return whether a token follows an empty for iterator 430 */ 431 private static boolean isFollowsEmptyForIterator(DetailAST ast) { 432 boolean result = false; 433 final DetailAST parent = ast.getParent(); 434 // Only traditional for statements are examined, not for-each statements 435 if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) { 436 final DetailAST forIterator = 437 parent.findFirstToken(TokenTypes.FOR_ITERATOR); 438 result = !forIterator.hasChildren(); 439 } 440 return result; 441 } 442 443 /** 444 * Checks that a token precedes an empty for initializer. 445 * 446 * @param ast the token to check 447 * @return whether a token precedes an empty for initializer 448 */ 449 private static boolean isPrecedingEmptyForInit(DetailAST ast) { 450 boolean result = false; 451 final DetailAST parent = ast.getParent(); 452 // Only traditional for statements are examined, not for-each statements 453 if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) { 454 final DetailAST forIterator = 455 parent.findFirstToken(TokenTypes.FOR_INIT); 456 result = !forIterator.hasChildren(); 457 } 458 return result; 459 } 460 461}