001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2021 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 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; 027 028/** 029 * <p> 030 * Checks that a token is surrounded by whitespace. Empty constructor, 031 * method, class, enum, interface, loop bodies (blocks), lambdas of the form 032 * </p> 033 * <pre> 034 * public MyClass() {} // empty constructor 035 * public void func() {} // empty method 036 * public interface Foo {} // empty interface 037 * public class Foo {} // empty class 038 * public enum Foo {} // empty enum 039 * MyClass c = new MyClass() {}; // empty anonymous class 040 * while (i = 1) {} // empty while loop 041 * for (int i = 1; i > 1; i++) {} // empty for loop 042 * do {} while (i = 1); // empty do-while loop 043 * Runnable noop = () -> {}; // empty lambda 044 * public @interface Beta {} // empty annotation type 045 * </pre> 046 * <p> 047 * may optionally be exempted from the policy using the {@code allowEmptyMethods}, 048 * {@code allowEmptyConstructors}, {@code allowEmptyTypes}, {@code allowEmptyLoops}, 049 * {@code allowEmptyLambdas} and {@code allowEmptyCatches} properties. 050 * </p> 051 * <p> 052 * This check does not flag as violation double brace initialization like: 053 * </p> 054 * <pre> 055 * new Properties() {{ 056 * setProperty("key", "value"); 057 * }}; 058 * </pre> 059 * <p> 060 * Parameter allowEmptyCatches allows to suppress violations when token list 061 * contains SLIST to check if beginning of block is surrounded by whitespace 062 * and catch block is empty, for example: 063 * </p> 064 * <pre> 065 * try { 066 * k = 5 / i; 067 * } catch (ArithmeticException ex) {} 068 * </pre> 069 * <p> 070 * With this property turned off, this raises violation because the beginning 071 * of the catch block (left curly bracket) is not separated from the end 072 * of the catch block (right curly bracket). 073 * </p> 074 * <ul> 075 * <li> 076 * Property {@code allowEmptyConstructors} - Allow empty constructor bodies. 077 * Type is {@code boolean}. 078 * Default value is {@code false}. 079 * </li> 080 * <li> 081 * Property {@code allowEmptyMethods} - Allow empty method bodies. 082 * Type is {@code boolean}. 083 * Default value is {@code false}. 084 * </li> 085 * <li> 086 * Property {@code allowEmptyTypes} - Allow empty class, interface and enum bodies. 087 * Type is {@code boolean}. 088 * Default value is {@code false}. 089 * </li> 090 * <li> 091 * Property {@code allowEmptyLoops} - Allow empty loop bodies. 092 * Type is {@code boolean}. 093 * Default value is {@code false}. 094 * </li> 095 * <li> 096 * Property {@code allowEmptyLambdas} - Allow empty lambda bodies. 097 * Type is {@code boolean}. 098 * Default value is {@code false}. 099 * </li> 100 * <li> 101 * Property {@code allowEmptyCatches} - Allow empty catch bodies. 102 * Type is {@code boolean}. 103 * Default value is {@code false}. 104 * </li> 105 * <li> 106 * Property {@code ignoreEnhancedForColon} - Ignore whitespace around colon in 107 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2"> 108 * enhanced for</a> loop. 109 * Type is {@code boolean}. 110 * Default value is {@code true}. 111 * </li> 112 * <li> 113 * Property {@code tokens} - tokens to check 114 * Type is {@code java.lang.String[]}. 115 * Validation type is {@code tokenSet}. 116 * Default value is: 117 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ASSIGN"> 118 * ASSIGN</a>, 119 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND"> 120 * BAND</a>, 121 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND_ASSIGN"> 122 * BAND_ASSIGN</a>, 123 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR"> 124 * BOR</a>, 125 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR_ASSIGN"> 126 * BOR_ASSIGN</a>, 127 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR"> 128 * BSR</a>, 129 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR_ASSIGN"> 130 * BSR_ASSIGN</a>, 131 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR"> 132 * BXOR</a>, 133 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR_ASSIGN"> 134 * BXOR_ASSIGN</a>, 135 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COLON"> 136 * COLON</a>, 137 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV"> 138 * DIV</a>, 139 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV_ASSIGN"> 140 * DIV_ASSIGN</a>, 141 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DO_WHILE"> 142 * DO_WHILE</a>, 143 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EQUAL"> 144 * EQUAL</a>, 145 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GE"> 146 * GE</a>, 147 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GT"> 148 * GT</a>, 149 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA"> 150 * LAMBDA</a>, 151 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAND"> 152 * LAND</a>, 153 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LCURLY"> 154 * LCURLY</a>, 155 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LE"> 156 * LE</a>, 157 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH"> 158 * LITERAL_CATCH</a>, 159 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO"> 160 * LITERAL_DO</a>, 161 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE"> 162 * LITERAL_ELSE</a>, 163 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FINALLY"> 164 * LITERAL_FINALLY</a>, 165 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR"> 166 * LITERAL_FOR</a>, 167 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF"> 168 * LITERAL_IF</a>, 169 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_RETURN"> 170 * LITERAL_RETURN</a>, 171 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH"> 172 * LITERAL_SWITCH</a>, 173 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED"> 174 * LITERAL_SYNCHRONIZED</a>, 175 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRY"> 176 * LITERAL_TRY</a>, 177 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE"> 178 * LITERAL_WHILE</a>, 179 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LOR"> 180 * LOR</a>, 181 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LT"> 182 * LT</a>, 183 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS"> 184 * MINUS</a>, 185 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS_ASSIGN"> 186 * MINUS_ASSIGN</a>, 187 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD"> 188 * MOD</a>, 189 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD_ASSIGN"> 190 * MOD_ASSIGN</a>, 191 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NOT_EQUAL"> 192 * NOT_EQUAL</a>, 193 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS"> 194 * PLUS</a>, 195 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS_ASSIGN"> 196 * PLUS_ASSIGN</a>, 197 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#QUESTION"> 198 * QUESTION</a>, 199 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RCURLY"> 200 * RCURLY</a>, 201 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL"> 202 * SL</a>, 203 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SLIST"> 204 * SLIST</a>, 205 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL_ASSIGN"> 206 * SL_ASSIGN</a>, 207 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR"> 208 * SR</a>, 209 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR_ASSIGN"> 210 * SR_ASSIGN</a>, 211 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR"> 212 * STAR</a>, 213 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR_ASSIGN"> 214 * STAR_ASSIGN</a>, 215 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ASSERT"> 216 * LITERAL_ASSERT</a>, 217 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#TYPE_EXTENSION_AND"> 218 * TYPE_EXTENSION_AND</a>. 219 * </li> 220 * </ul> 221 * <p>To configure the check: 222 * </p> 223 * <pre> 224 * <module name="WhitespaceAround"/> 225 * </pre> 226 * <p>Example: 227 * </p> 228 * <pre> 229 * class Test { 230 * public Test(){} // 2 violations, '{' is not followed and preceded by whitespace. 231 * public static void main(String[] args) { 232 * if (foo) { // ok 233 * // body 234 * } 235 * else{ // violation 236 * // body 237 * } 238 * 239 * for (int i = 1; i > 1; i++) {} // violation, '{' is not followed by whitespace. 240 * 241 * Runnable noop = () ->{}; // 2 violations, 242 * // '{' is not followed and preceded by whitespace. 243 * try { 244 * // body 245 * } catch (Exception e){} // 2 violations, 246 * // '{' is not followed and preceded by whitespace. 247 * 248 * char[] vowels = {'a', 'e', 'i', 'o', 'u'}; 249 * for (char item: vowels) { // ok, because ignoreEnhancedForColon is true by default 250 * // body 251 * } 252 * } 253 * } 254 * </pre> 255 * <p>To configure the check for whitespace only around 256 * assignment operators: 257 * </p> 258 * <pre> 259 * <module name="WhitespaceAround"> 260 * <property name="tokens" 261 * value="ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN, 262 * MOD_ASSIGN,SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN, 263 * BOR_ASSIGN,BAND_ASSIGN"/> 264 * </module> 265 * </pre> 266 * <p>Example: 267 * </p> 268 * <pre> 269 * class Test { 270 * public static void main(String[] args) { 271 * int b=10; // violation 272 * int c = 10; // ok 273 * b+=10; // violation 274 * b += 10; // ok 275 * c*=10; // violation 276 * c *= 10; // ok 277 * c-=5; // violation 278 * c -= 5; // ok 279 * c/=2; // violation 280 * c /= 2; // ok 281 * c%=1; // violation 282 * c %= 1; // ok 283 * c>>=1; // violation 284 * c >>= 1; // ok 285 * c>>>=1; // violation 286 * c >>>= 1; // ok 287 * } 288 * public void myFunction() { 289 * c^=1; // violation 290 * c ^= 1; // ok 291 * c|=1; // violation 292 * c |= 1; // ok 293 * c&=1; // violation 294 * c &= 1; // ok 295 * c<<=1; // violation 296 * c <<= 1; // ok 297 * } 298 * } 299 * </pre> 300 * <p>To configure the check for whitespace only around curly braces: 301 * </p> 302 * <pre> 303 * <module name="WhitespaceAround"> 304 * <property name="tokens" value="LCURLY,RCURLY"/> 305 * </module> 306 * </pre> 307 * <p>Example: 308 * </p> 309 * <pre> 310 * class Test { 311 * public void myFunction() {} // violation 312 * public void myFunction() { } // ok 313 * } 314 * </pre> 315 * <p> 316 * To configure the check to allow empty method bodies: 317 * </p> 318 * <pre> 319 * <module name="WhitespaceAround"> 320 * <property name="allowEmptyMethods" value="true"/> 321 * </module> 322 * </pre> 323 * <p>Example: 324 * </p> 325 * <pre> 326 * class Test { 327 * public void muFunction() {} // ok 328 * int a=4; // 2 violations, '=' is not followed and preceded by whitespace. 329 * } 330 * </pre> 331 * <p> 332 * To configure the check to allow empty constructor bodies: 333 * </p> 334 * <pre> 335 * <module name="WhitespaceAround"> 336 * <property name="allowEmptyConstructors" value="true"/> 337 * </module> 338 * </pre> 339 * <p>Example: 340 * </p> 341 * <pre> 342 * class Test { 343 * public Test() {} // ok 344 * public void muFunction() {} // violation, '{' is not followed by whitespace. 345 * } 346 * </pre> 347 * <p> 348 * To configure the check to allow empty type bodies: 349 * </p> 350 * <pre> 351 * <module name="WhitespaceAround"> 352 * <property name="allowEmptyTypes" value="true"/> 353 * </module> 354 * </pre> 355 * <p>Example: 356 * </p> 357 * <pre> 358 * class Test {} // ok 359 * interface testInterface{} // ok 360 * class anotherTest { 361 * int a=4; // 2 violations, '=' is not followed and preceded by whitespace. 362 * } 363 * </pre> 364 * <p> 365 * To configure the check to allow empty loop bodies: 366 * </p> 367 * <pre> 368 * <module name="WhitespaceAround"> 369 * <property name="allowEmptyLoops" value="true"/> 370 * </module> 371 * </pre> 372 * <p>Example: 373 * </p> 374 * <pre> 375 * class Test { 376 * public static void main(String[] args) { 377 * for (int i = 100;i > 10; i--){} // ok 378 * do {} while (i = 1); // ok 379 * int a=4; // 2 violations, '=' is not followed and preceded by whitespace. 380 * } 381 * } 382 * </pre> 383 * <p> 384 * To configure the check to allow empty lambda bodies: 385 * </p> 386 * <pre> 387 * <module name="WhitespaceAround"> 388 * <property name="allowEmptyLambdas" value="true"/> 389 * </module> 390 * </pre> 391 * <p>Example: 392 * </p> 393 * <pre> 394 * class Test { 395 * public static void main(String[] args) { 396 * Runnable noop = () -> {}; // ok 397 * int a=4; // 2 violations, '=' is not followed and preceded by whitespace. 398 * } 399 * } 400 * </pre> 401 * <p> 402 * To configure the check to allow empty catch bodies: 403 * </p> 404 * <pre> 405 * <module name="WhitespaceAround"> 406 * <property name="allowEmptyCatches" value="true"/> 407 * </module> 408 * </pre> 409 * <p>Example: 410 * </p> 411 * <pre> 412 * class Test { 413 * public static void main(String[] args) { 414 * int a=4; // 2 violations, '=' is not followed and preceded by whitespace. 415 * try { 416 * // body 417 * } catch (Exception e){} // ok 418 * } 419 * } 420 * </pre> 421 * <p> 422 * Also, this check can be configured to ignore the colon in an enhanced for 423 * loop. The colon in an enhanced for loop is ignored by default. 424 * </p> 425 * <p> 426 * To configure the check to ignore the colon: 427 * </p> 428 * <pre> 429 * <module name="WhitespaceAround"> 430 * <property name="ignoreEnhancedForColon" value="false" /> 431 * </module> 432 * </pre> 433 * <p>Example: 434 * </p> 435 * <pre> 436 * class Test { 437 * public static void main(String[] args) { 438 * int a=4; // 2 violations , '=' is not followed and preceded by whitespace. 439 * char[] vowels = {'a', 'e', 'i', 'o', 'u'}; 440 * for (char item: vowels) { // violation, ':' is not preceded by whitespace. 441 * // body 442 * } 443 * } 444 * } 445 * </pre> 446 * <p> 447 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 448 * </p> 449 * <p> 450 * Violation Message Keys: 451 * </p> 452 * <ul> 453 * <li> 454 * {@code ws.notFollowed} 455 * </li> 456 * <li> 457 * {@code ws.notPreceded} 458 * </li> 459 * </ul> 460 * 461 * @since 3.0 462 */ 463@StatelessCheck 464public class WhitespaceAroundCheck extends AbstractCheck { 465 466 /** 467 * A key is pointing to the warning message text in "messages.properties" 468 * file. 469 */ 470 public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded"; 471 472 /** 473 * A key is pointing to the warning message text in "messages.properties" 474 * file. 475 */ 476 public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed"; 477 478 /** Allow empty constructor bodies. */ 479 private boolean allowEmptyConstructors; 480 /** Allow empty method bodies. */ 481 private boolean allowEmptyMethods; 482 /** Allow empty class, interface and enum bodies. */ 483 private boolean allowEmptyTypes; 484 /** Allow empty loop bodies. */ 485 private boolean allowEmptyLoops; 486 /** Allow empty lambda bodies. */ 487 private boolean allowEmptyLambdas; 488 /** Allow empty catch bodies. */ 489 private boolean allowEmptyCatches; 490 /** 491 * Ignore whitespace around colon in 492 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2"> 493 * enhanced for</a> loop. 494 */ 495 private boolean ignoreEnhancedForColon = true; 496 497 @Override 498 public int[] getDefaultTokens() { 499 return new int[] { 500 TokenTypes.ASSIGN, 501 TokenTypes.BAND, 502 TokenTypes.BAND_ASSIGN, 503 TokenTypes.BOR, 504 TokenTypes.BOR_ASSIGN, 505 TokenTypes.BSR, 506 TokenTypes.BSR_ASSIGN, 507 TokenTypes.BXOR, 508 TokenTypes.BXOR_ASSIGN, 509 TokenTypes.COLON, 510 TokenTypes.DIV, 511 TokenTypes.DIV_ASSIGN, 512 TokenTypes.DO_WHILE, 513 TokenTypes.EQUAL, 514 TokenTypes.GE, 515 TokenTypes.GT, 516 TokenTypes.LAMBDA, 517 TokenTypes.LAND, 518 TokenTypes.LCURLY, 519 TokenTypes.LE, 520 TokenTypes.LITERAL_CATCH, 521 TokenTypes.LITERAL_DO, 522 TokenTypes.LITERAL_ELSE, 523 TokenTypes.LITERAL_FINALLY, 524 TokenTypes.LITERAL_FOR, 525 TokenTypes.LITERAL_IF, 526 TokenTypes.LITERAL_RETURN, 527 TokenTypes.LITERAL_SWITCH, 528 TokenTypes.LITERAL_SYNCHRONIZED, 529 TokenTypes.LITERAL_TRY, 530 TokenTypes.LITERAL_WHILE, 531 TokenTypes.LOR, 532 TokenTypes.LT, 533 TokenTypes.MINUS, 534 TokenTypes.MINUS_ASSIGN, 535 TokenTypes.MOD, 536 TokenTypes.MOD_ASSIGN, 537 TokenTypes.NOT_EQUAL, 538 TokenTypes.PLUS, 539 TokenTypes.PLUS_ASSIGN, 540 TokenTypes.QUESTION, 541 TokenTypes.RCURLY, 542 TokenTypes.SL, 543 TokenTypes.SLIST, 544 TokenTypes.SL_ASSIGN, 545 TokenTypes.SR, 546 TokenTypes.SR_ASSIGN, 547 TokenTypes.STAR, 548 TokenTypes.STAR_ASSIGN, 549 TokenTypes.LITERAL_ASSERT, 550 TokenTypes.TYPE_EXTENSION_AND, 551 }; 552 } 553 554 @Override 555 public int[] getAcceptableTokens() { 556 return new int[] { 557 TokenTypes.ASSIGN, 558 TokenTypes.ARRAY_INIT, 559 TokenTypes.BAND, 560 TokenTypes.BAND_ASSIGN, 561 TokenTypes.BOR, 562 TokenTypes.BOR_ASSIGN, 563 TokenTypes.BSR, 564 TokenTypes.BSR_ASSIGN, 565 TokenTypes.BXOR, 566 TokenTypes.BXOR_ASSIGN, 567 TokenTypes.COLON, 568 TokenTypes.DIV, 569 TokenTypes.DIV_ASSIGN, 570 TokenTypes.DO_WHILE, 571 TokenTypes.EQUAL, 572 TokenTypes.GE, 573 TokenTypes.GT, 574 TokenTypes.LAMBDA, 575 TokenTypes.LAND, 576 TokenTypes.LCURLY, 577 TokenTypes.LE, 578 TokenTypes.LITERAL_CATCH, 579 TokenTypes.LITERAL_DO, 580 TokenTypes.LITERAL_ELSE, 581 TokenTypes.LITERAL_FINALLY, 582 TokenTypes.LITERAL_FOR, 583 TokenTypes.LITERAL_IF, 584 TokenTypes.LITERAL_RETURN, 585 TokenTypes.LITERAL_SWITCH, 586 TokenTypes.LITERAL_SYNCHRONIZED, 587 TokenTypes.LITERAL_TRY, 588 TokenTypes.LITERAL_WHILE, 589 TokenTypes.LOR, 590 TokenTypes.LT, 591 TokenTypes.MINUS, 592 TokenTypes.MINUS_ASSIGN, 593 TokenTypes.MOD, 594 TokenTypes.MOD_ASSIGN, 595 TokenTypes.NOT_EQUAL, 596 TokenTypes.PLUS, 597 TokenTypes.PLUS_ASSIGN, 598 TokenTypes.QUESTION, 599 TokenTypes.RCURLY, 600 TokenTypes.SL, 601 TokenTypes.SLIST, 602 TokenTypes.SL_ASSIGN, 603 TokenTypes.SR, 604 TokenTypes.SR_ASSIGN, 605 TokenTypes.STAR, 606 TokenTypes.STAR_ASSIGN, 607 TokenTypes.LITERAL_ASSERT, 608 TokenTypes.TYPE_EXTENSION_AND, 609 TokenTypes.WILDCARD_TYPE, 610 TokenTypes.GENERIC_START, 611 TokenTypes.GENERIC_END, 612 TokenTypes.ELLIPSIS, 613 }; 614 } 615 616 @Override 617 public int[] getRequiredTokens() { 618 return CommonUtil.EMPTY_INT_ARRAY; 619 } 620 621 /** 622 * Setter to allow empty method bodies. 623 * 624 * @param allow {@code true} to allow empty method bodies. 625 */ 626 public void setAllowEmptyMethods(boolean allow) { 627 allowEmptyMethods = allow; 628 } 629 630 /** 631 * Setter to allow empty constructor bodies. 632 * 633 * @param allow {@code true} to allow empty constructor bodies. 634 */ 635 public void setAllowEmptyConstructors(boolean allow) { 636 allowEmptyConstructors = allow; 637 } 638 639 /** 640 * Setter to ignore whitespace around colon in 641 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2"> 642 * enhanced for</a> loop. 643 * 644 * @param ignore {@code true} to ignore enhanced for colon. 645 */ 646 public void setIgnoreEnhancedForColon(boolean ignore) { 647 ignoreEnhancedForColon = ignore; 648 } 649 650 /** 651 * Setter to allow empty class, interface and enum bodies. 652 * 653 * @param allow {@code true} to allow empty type bodies. 654 */ 655 public void setAllowEmptyTypes(boolean allow) { 656 allowEmptyTypes = allow; 657 } 658 659 /** 660 * Setter to allow empty loop bodies. 661 * 662 * @param allow {@code true} to allow empty loops bodies. 663 */ 664 public void setAllowEmptyLoops(boolean allow) { 665 allowEmptyLoops = allow; 666 } 667 668 /** 669 * Setter to allow empty lambda bodies. 670 * 671 * @param allow {@code true} to allow empty lambda expressions. 672 */ 673 public void setAllowEmptyLambdas(boolean allow) { 674 allowEmptyLambdas = allow; 675 } 676 677 /** 678 * Setter to allow empty catch bodies. 679 * 680 * @param allow {@code true} to allow empty catch blocks. 681 */ 682 public void setAllowEmptyCatches(boolean allow) { 683 allowEmptyCatches = allow; 684 } 685 686 @Override 687 public void visitToken(DetailAST ast) { 688 final int currentType = ast.getType(); 689 if (!isNotRelevantSituation(ast, currentType)) { 690 final String line = getLine(ast.getLineNo() - 1); 691 final int before = ast.getColumnNo() - 1; 692 final int after = ast.getColumnNo() + ast.getText().length(); 693 694 if (before >= 0) { 695 final char prevChar = line.charAt(before); 696 if (shouldCheckSeparationFromPreviousToken(ast) 697 && !Character.isWhitespace(prevChar)) { 698 log(ast, MSG_WS_NOT_PRECEDED, ast.getText()); 699 } 700 } 701 702 if (after < line.length()) { 703 final char nextChar = line.charAt(after); 704 if (shouldCheckSeparationFromNextToken(ast, nextChar) 705 && !Character.isWhitespace(nextChar)) { 706 log(ast, MSG_WS_NOT_FOLLOWED, ast.getText()); 707 } 708 } 709 } 710 } 711 712 /** 713 * Is ast not a target of Check. 714 * 715 * @param ast ast 716 * @param currentType type of ast 717 * @return true is ok to skip validation 718 */ 719 private boolean isNotRelevantSituation(DetailAST ast, int currentType) { 720 final int parentType = ast.getParent().getType(); 721 final boolean starImport = currentType == TokenTypes.STAR 722 && parentType == TokenTypes.DOT; 723 final boolean insideCaseGroup = parentType == TokenTypes.CASE_GROUP; 724 725 final boolean starImportOrSlistInsideCaseGroup = starImport || insideCaseGroup; 726 final boolean colonOfCaseOrDefaultOrForEach = 727 isColonOfCaseOrDefault(parentType) 728 || isColonOfForEach(parentType); 729 final boolean emptyBlockOrType = 730 isEmptyBlock(ast, parentType) 731 || allowEmptyTypes && isEmptyType(ast); 732 733 return starImportOrSlistInsideCaseGroup 734 || colonOfCaseOrDefaultOrForEach 735 || emptyBlockOrType 736 || isArrayInitialization(currentType, parentType); 737 } 738 739 /** 740 * Check if it should be checked if previous token is separated from current by 741 * whitespace. 742 * This function is needed to recognise double brace initialization as valid, 743 * unfortunately its not possible to implement this functionality 744 * in isNotRelevantSituation method, because in this method when we return 745 * true(is not relevant) ast is later doesn't check at all. For example: 746 * new Properties() {{setProperty("double curly braces", "are not a style violation"); 747 * }}; 748 * For second left curly brace in first line when we would return true from 749 * isNotRelevantSituation it wouldn't later check that the next token(setProperty) 750 * is not separated from previous token. 751 * 752 * @param ast current AST. 753 * @return true if it should be checked if previous token is separated by whitespace, 754 * false otherwise. 755 */ 756 private static boolean shouldCheckSeparationFromPreviousToken(DetailAST ast) { 757 return !isPartOfDoubleBraceInitializerForPreviousToken(ast); 758 } 759 760 /** 761 * Check if it should be checked if next token is separated from current by 762 * whitespace. Explanation why this method is needed is identical to one 763 * included in shouldCheckSeparationFromPreviousToken method. 764 * 765 * @param ast current AST. 766 * @param nextChar next character. 767 * @return true if it should be checked if next token is separated by whitespace, 768 * false otherwise. 769 */ 770 private boolean shouldCheckSeparationFromNextToken(DetailAST ast, char nextChar) { 771 return !isEmptyCtorBlockCheckedFromSlist(ast) 772 && !(ast.getType() == TokenTypes.LITERAL_RETURN 773 && ast.getFirstChild().getType() == TokenTypes.SEMI) 774 && ast.getType() != TokenTypes.ARRAY_INIT 775 && !isAnonymousInnerClassEnd(ast.getType(), nextChar) 776 && !isPartOfDoubleBraceInitializerForNextToken(ast); 777 } 778 779 /** 780 * Check for "})" or "};" or "},". Happens with anon-inners 781 * 782 * @param currentType token 783 * @param nextChar next symbol 784 * @return true is that is end of anon inner class 785 */ 786 private static boolean isAnonymousInnerClassEnd(int currentType, char nextChar) { 787 return currentType == TokenTypes.RCURLY 788 && (nextChar == ')' 789 || nextChar == ';' 790 || nextChar == ',' 791 || nextChar == '.'); 792 } 793 794 /** 795 * Is empty block. 796 * 797 * @param ast ast 798 * @param parentType parent 799 * @return true is block is empty 800 */ 801 private boolean isEmptyBlock(DetailAST ast, int parentType) { 802 return isEmptyMethodBlock(ast, parentType) 803 || isEmptyCtorBlockCheckedFromRcurly(ast) 804 || isEmptyLoop(ast, parentType) 805 || isEmptyLambda(ast, parentType) 806 || isEmptyCatch(ast, parentType); 807 } 808 809 /** 810 * Tests if a given {@code DetailAST} is part of an empty block. 811 * An example empty block might look like the following 812 * <p> 813 * <pre> public void myMethod(int val) {}</pre> 814 * </p> 815 * In the above, the method body is an empty block ("{}"). 816 * 817 * @param ast the {@code DetailAST} to test. 818 * @param parentType the token type of {@code ast}'s parent. 819 * @param match the parent token type we're looking to match. 820 * @return {@code true} if {@code ast} makes up part of an 821 * empty block contained under a {@code match} token type 822 * node. 823 */ 824 private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) { 825 final boolean result; 826 final int type = ast.getType(); 827 if (type == TokenTypes.RCURLY) { 828 final DetailAST parent = ast.getParent(); 829 final DetailAST grandParent = ast.getParent().getParent(); 830 result = parent.getFirstChild().getType() == TokenTypes.RCURLY 831 && grandParent.getType() == match; 832 } 833 else { 834 result = type == TokenTypes.SLIST 835 && parentType == match 836 && ast.getFirstChild().getType() == TokenTypes.RCURLY; 837 } 838 return result; 839 } 840 841 /** 842 * Whether colon belongs to cases or defaults. 843 * 844 * @param parentType parent 845 * @return true if current token in colon of case or default tokens 846 */ 847 private static boolean isColonOfCaseOrDefault(int parentType) { 848 return parentType == TokenTypes.LITERAL_DEFAULT 849 || parentType == TokenTypes.LITERAL_CASE; 850 } 851 852 /** 853 * Whether colon belongs to for-each. 854 * 855 * @param parentType parent 856 * @return true if current token in colon of for-each token 857 */ 858 private boolean isColonOfForEach(int parentType) { 859 return parentType == TokenTypes.FOR_EACH_CLAUSE 860 && ignoreEnhancedForColon; 861 } 862 863 /** 864 * Is array initialization. 865 * 866 * @param currentType current token 867 * @param parentType parent token 868 * @return true is current token inside array initialization 869 */ 870 private static boolean isArrayInitialization(int currentType, int parentType) { 871 return currentType == TokenTypes.RCURLY 872 && (parentType == TokenTypes.ARRAY_INIT 873 || parentType == TokenTypes.ANNOTATION_ARRAY_INIT); 874 } 875 876 /** 877 * Test if the given {@code DetailAST} is part of an allowed empty 878 * method block. 879 * 880 * @param ast the {@code DetailAST} to test. 881 * @param parentType the token type of {@code ast}'s parent. 882 * @return {@code true} if {@code ast} makes up part of an 883 * allowed empty method block. 884 */ 885 private boolean isEmptyMethodBlock(DetailAST ast, int parentType) { 886 return allowEmptyMethods 887 && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF); 888 } 889 890 /** 891 * Test if the given {@code DetailAST} is part of an allowed empty 892 * constructor (ctor) block checked from RCURLY. 893 * 894 * @param ast the {@code DetailAST} to test. 895 * @return {@code true} if {@code ast} makes up part of an 896 * allowed empty constructor block. 897 */ 898 private boolean isEmptyCtorBlockCheckedFromRcurly(DetailAST ast) { 899 final DetailAST parent = ast.getParent(); 900 final DetailAST grandParent = ast.getParent().getParent(); 901 return allowEmptyConstructors 902 && parent.getFirstChild().getType() == TokenTypes.RCURLY 903 && (grandParent.getType() == TokenTypes.CTOR_DEF 904 || grandParent.getType() == TokenTypes.COMPACT_CTOR_DEF); 905 906 } 907 908 /** 909 * Test if the given {@code DetailAST} is a part of an allowed 910 * empty constructor checked from SLIST token. 911 * 912 * @param ast the {@code DetailAST} to test. 913 * @return {@code true} if {@code ast} makes up part of an 914 * empty constructor block. 915 */ 916 private boolean isEmptyCtorBlockCheckedFromSlist(DetailAST ast) { 917 return allowEmptyConstructors 918 && (ast.getParent().getType() == TokenTypes.CTOR_DEF 919 || ast.getParent().getType() == TokenTypes.COMPACT_CTOR_DEF) 920 && ast.getFirstChild().getType() == TokenTypes.RCURLY; 921 } 922 923 /** 924 * Checks if loop is empty. 925 * 926 * @param ast ast the {@code DetailAST} to test. 927 * @param parentType the token type of {@code ast}'s parent. 928 * @return {@code true} if {@code ast} makes up part of an 929 * allowed empty loop block. 930 */ 931 private boolean isEmptyLoop(DetailAST ast, int parentType) { 932 return allowEmptyLoops 933 && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR) 934 || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_WHILE) 935 || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_DO)); 936 } 937 938 /** 939 * Test if the given {@code DetailAST} is part of an allowed empty 940 * lambda block. 941 * 942 * @param ast the {@code DetailAST} to test. 943 * @param parentType the token type of {@code ast}'s parent. 944 * @return {@code true} if {@code ast} makes up part of an 945 * allowed empty lambda block. 946 */ 947 private boolean isEmptyLambda(DetailAST ast, int parentType) { 948 return allowEmptyLambdas && isEmptyBlock(ast, parentType, TokenTypes.LAMBDA); 949 } 950 951 /** 952 * Tests if the given {@code DetailAst} is part of an allowed empty 953 * catch block. 954 * 955 * @param ast the {@code DetailAst} to test. 956 * @param parentType the token type of {@code ast}'s parent 957 * @return {@code true} if {@code ast} makes up part of an 958 * allowed empty catch block. 959 */ 960 private boolean isEmptyCatch(DetailAST ast, int parentType) { 961 return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH); 962 } 963 964 /** 965 * Test if the given {@code DetailAST} is part of an empty block. 966 * An example empty block might look like the following 967 * <p> 968 * <pre> class Foo {}</pre> 969 * </p> 970 * 971 * @param ast ast the {@code DetailAST} to test. 972 * @return {@code true} if {@code ast} makes up part of an 973 * empty block contained under a {@code match} token type 974 * node. 975 */ 976 private static boolean isEmptyType(DetailAST ast) { 977 final int type = ast.getType(); 978 final DetailAST nextSibling = ast.getNextSibling(); 979 final DetailAST previousSibling = ast.getPreviousSibling(); 980 return type == TokenTypes.LCURLY 981 && nextSibling.getType() == TokenTypes.RCURLY 982 || previousSibling != null 983 && previousSibling.getType() == TokenTypes.LCURLY; 984 } 985 986 /** 987 * Check if given ast is part of double brace initializer and if it 988 * should omit checking if previous token is separated by whitespace. 989 * 990 * @param ast ast to check 991 * @return true if it should omit checking for previous token, false otherwise 992 */ 993 private static boolean isPartOfDoubleBraceInitializerForPreviousToken(DetailAST ast) { 994 final boolean initializerBeginsAfterClassBegins = 995 ast.getParent().getType() == TokenTypes.INSTANCE_INIT; 996 final boolean classEndsAfterInitializerEnds = ast.getPreviousSibling() != null 997 && ast.getPreviousSibling().getType() == TokenTypes.INSTANCE_INIT; 998 return initializerBeginsAfterClassBegins || classEndsAfterInitializerEnds; 999 } 1000 1001 /** 1002 * Check if given ast is part of double brace initializer and if it 1003 * should omit checking if next token is separated by whitespace. 1004 * See <a href="https://github.com/checkstyle/checkstyle/pull/2845"> 1005 * PR#2845</a> for more information why this function was needed. 1006 * 1007 * @param ast ast to check 1008 * @return true if it should omit checking for next token, false otherwise 1009 */ 1010 private static boolean isPartOfDoubleBraceInitializerForNextToken(DetailAST ast) { 1011 final boolean classBeginBeforeInitializerBegin = ast.getType() == TokenTypes.LCURLY 1012 && ast.getNextSibling().getType() == TokenTypes.INSTANCE_INIT; 1013 final boolean initializerEndsBeforeClassEnds = 1014 ast.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT 1015 && ast.getParent().getParent().getNextSibling().getType() == TokenTypes.RCURLY; 1016 return classBeginBeforeInitializerBegin || initializerEndsBeforeClassEnds; 1017 } 1018 1019}