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