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 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 final int[] codePoints = line.codePoints().toArray(); 694 695 if (before >= 0) { 696 final char prevChar = Character.toChars(codePoints[before])[0]; 697 if (shouldCheckSeparationFromPreviousToken(ast) 698 && !Character.isWhitespace(prevChar)) { 699 log(ast, MSG_WS_NOT_PRECEDED, ast.getText()); 700 } 701 } 702 703 if (after < codePoints.length) { 704 final char nextChar = Character.toChars(codePoints[after])[0]; 705 if (shouldCheckSeparationFromNextToken(ast, nextChar) 706 && !Character.isWhitespace(nextChar)) { 707 log(ast, MSG_WS_NOT_FOLLOWED, ast.getText()); 708 } 709 } 710 } 711 } 712 713 /** 714 * Is ast not a target of Check. 715 * 716 * @param ast ast 717 * @param currentType type of ast 718 * @return true is ok to skip validation 719 */ 720 private boolean isNotRelevantSituation(DetailAST ast, int currentType) { 721 final int parentType = ast.getParent().getType(); 722 final boolean starImport = currentType == TokenTypes.STAR 723 && parentType == TokenTypes.DOT; 724 final boolean insideCaseGroup = parentType == TokenTypes.CASE_GROUP; 725 726 final boolean starImportOrSlistInsideCaseGroup = starImport || insideCaseGroup; 727 final boolean colonOfCaseOrDefaultOrForEach = 728 isColonOfCaseOrDefault(parentType) 729 || isColonOfForEach(parentType); 730 final boolean emptyBlockOrType = 731 isEmptyBlock(ast, parentType) 732 || allowEmptyTypes && isEmptyType(ast); 733 734 return starImportOrSlistInsideCaseGroup 735 || colonOfCaseOrDefaultOrForEach 736 || emptyBlockOrType 737 || isArrayInitialization(currentType, parentType); 738 } 739 740 /** 741 * Check if it should be checked if previous token is separated from current by 742 * whitespace. 743 * This function is needed to recognise double brace initialization as valid, 744 * unfortunately its not possible to implement this functionality 745 * in isNotRelevantSituation method, because in this method when we return 746 * true(is not relevant) ast is later doesn't check at all. For example: 747 * new Properties() {{setProperty("double curly braces", "are not a style violation"); 748 * }}; 749 * For second left curly brace in first line when we would return true from 750 * isNotRelevantSituation it wouldn't later check that the next token(setProperty) 751 * is not separated from previous token. 752 * 753 * @param ast current AST. 754 * @return true if it should be checked if previous token is separated by whitespace, 755 * false otherwise. 756 */ 757 private static boolean shouldCheckSeparationFromPreviousToken(DetailAST ast) { 758 return !isPartOfDoubleBraceInitializerForPreviousToken(ast); 759 } 760 761 /** 762 * Check if it should be checked if next token is separated from current by 763 * whitespace. Explanation why this method is needed is identical to one 764 * included in shouldCheckSeparationFromPreviousToken method. 765 * 766 * @param ast current AST. 767 * @param nextChar next character. 768 * @return true if it should be checked if next token is separated by whitespace, 769 * false otherwise. 770 */ 771 private boolean shouldCheckSeparationFromNextToken(DetailAST ast, char nextChar) { 772 return !isEmptyCtorBlockCheckedFromSlist(ast) 773 && !(ast.getType() == TokenTypes.LITERAL_RETURN 774 && ast.getFirstChild().getType() == TokenTypes.SEMI) 775 && ast.getType() != TokenTypes.ARRAY_INIT 776 && !isAnonymousInnerClassEnd(ast.getType(), nextChar) 777 && !isPartOfDoubleBraceInitializerForNextToken(ast); 778 } 779 780 /** 781 * Check for "})" or "};" or "},". Happens with anon-inners 782 * 783 * @param currentType token 784 * @param nextChar next symbol 785 * @return true is that is end of anon inner class 786 */ 787 private static boolean isAnonymousInnerClassEnd(int currentType, char nextChar) { 788 return currentType == TokenTypes.RCURLY 789 && (nextChar == ')' 790 || nextChar == ';' 791 || nextChar == ',' 792 || nextChar == '.'); 793 } 794 795 /** 796 * Is empty block. 797 * 798 * @param ast ast 799 * @param parentType parent 800 * @return true is block is empty 801 */ 802 private boolean isEmptyBlock(DetailAST ast, int parentType) { 803 return isEmptyMethodBlock(ast, parentType) 804 || isEmptyCtorBlockCheckedFromRcurly(ast) 805 || isEmptyLoop(ast, parentType) 806 || isEmptyLambda(ast, parentType) 807 || isEmptyCatch(ast, parentType); 808 } 809 810 /** 811 * Tests if a given {@code DetailAST} is part of an empty block. 812 * An example empty block might look like the following 813 * <pre> public void myMethod(int val) {}</pre> 814 * In the above, the method body is an empty block ("{}"). 815 * 816 * @param ast the {@code DetailAST} to test. 817 * @param parentType the token type of {@code ast}'s parent. 818 * @param match the parent token type we're looking to match. 819 * @return {@code true} if {@code ast} makes up part of an 820 * empty block contained under a {@code match} token type 821 * node. 822 */ 823 private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) { 824 final boolean result; 825 final int type = ast.getType(); 826 if (type == TokenTypes.RCURLY) { 827 final DetailAST parent = ast.getParent(); 828 final DetailAST grandParent = ast.getParent().getParent(); 829 result = parent.getFirstChild().getType() == TokenTypes.RCURLY 830 && grandParent.getType() == match; 831 } 832 else { 833 result = type == TokenTypes.SLIST 834 && parentType == match 835 && ast.getFirstChild().getType() == TokenTypes.RCURLY; 836 } 837 return result; 838 } 839 840 /** 841 * Whether colon belongs to cases or defaults. 842 * 843 * @param parentType parent 844 * @return true if current token in colon of case or default tokens 845 */ 846 private static boolean isColonOfCaseOrDefault(int parentType) { 847 return parentType == TokenTypes.LITERAL_DEFAULT 848 || parentType == TokenTypes.LITERAL_CASE; 849 } 850 851 /** 852 * Whether colon belongs to for-each. 853 * 854 * @param parentType parent 855 * @return true if current token in colon of for-each token 856 */ 857 private boolean isColonOfForEach(int parentType) { 858 return parentType == TokenTypes.FOR_EACH_CLAUSE 859 && ignoreEnhancedForColon; 860 } 861 862 /** 863 * Is array initialization. 864 * 865 * @param currentType current token 866 * @param parentType parent token 867 * @return true is current token inside array initialization 868 */ 869 private static boolean isArrayInitialization(int currentType, int parentType) { 870 return currentType == TokenTypes.RCURLY 871 && (parentType == TokenTypes.ARRAY_INIT 872 || parentType == TokenTypes.ANNOTATION_ARRAY_INIT); 873 } 874 875 /** 876 * Test if the given {@code DetailAST} is part of an allowed empty 877 * method block. 878 * 879 * @param ast the {@code DetailAST} to test. 880 * @param parentType the token type of {@code ast}'s parent. 881 * @return {@code true} if {@code ast} makes up part of an 882 * allowed empty method block. 883 */ 884 private boolean isEmptyMethodBlock(DetailAST ast, int parentType) { 885 return allowEmptyMethods 886 && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF); 887 } 888 889 /** 890 * Test if the given {@code DetailAST} is part of an allowed empty 891 * constructor (ctor) block checked from RCURLY. 892 * 893 * @param ast the {@code DetailAST} to test. 894 * @return {@code true} if {@code ast} makes up part of an 895 * allowed empty constructor block. 896 */ 897 private boolean isEmptyCtorBlockCheckedFromRcurly(DetailAST ast) { 898 final DetailAST parent = ast.getParent(); 899 final DetailAST grandParent = ast.getParent().getParent(); 900 return allowEmptyConstructors 901 && parent.getFirstChild().getType() == TokenTypes.RCURLY 902 && (grandParent.getType() == TokenTypes.CTOR_DEF 903 || grandParent.getType() == TokenTypes.COMPACT_CTOR_DEF); 904 905 } 906 907 /** 908 * Test if the given {@code DetailAST} is a part of an allowed 909 * empty constructor checked from SLIST token. 910 * 911 * @param ast the {@code DetailAST} to test. 912 * @return {@code true} if {@code ast} makes up part of an 913 * empty constructor block. 914 */ 915 private boolean isEmptyCtorBlockCheckedFromSlist(DetailAST ast) { 916 return allowEmptyConstructors 917 && (ast.getParent().getType() == TokenTypes.CTOR_DEF 918 || ast.getParent().getType() == TokenTypes.COMPACT_CTOR_DEF) 919 && ast.getFirstChild().getType() == TokenTypes.RCURLY; 920 } 921 922 /** 923 * Checks if loop is empty. 924 * 925 * @param ast ast the {@code DetailAST} to test. 926 * @param parentType the token type of {@code ast}'s parent. 927 * @return {@code true} if {@code ast} makes up part of an 928 * allowed empty loop block. 929 */ 930 private boolean isEmptyLoop(DetailAST ast, int parentType) { 931 return allowEmptyLoops 932 && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR) 933 || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_WHILE) 934 || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_DO)); 935 } 936 937 /** 938 * Test if the given {@code DetailAST} is part of an allowed empty 939 * lambda block. 940 * 941 * @param ast the {@code DetailAST} to test. 942 * @param parentType the token type of {@code ast}'s parent. 943 * @return {@code true} if {@code ast} makes up part of an 944 * allowed empty lambda block. 945 */ 946 private boolean isEmptyLambda(DetailAST ast, int parentType) { 947 return allowEmptyLambdas && isEmptyBlock(ast, parentType, TokenTypes.LAMBDA); 948 } 949 950 /** 951 * Tests if the given {@code DetailAst} is part of an allowed empty 952 * catch block. 953 * 954 * @param ast the {@code DetailAst} to test. 955 * @param parentType the token type of {@code ast}'s parent 956 * @return {@code true} if {@code ast} makes up part of an 957 * allowed empty catch block. 958 */ 959 private boolean isEmptyCatch(DetailAST ast, int parentType) { 960 return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH); 961 } 962 963 /** 964 * Test if the given {@code DetailAST} is part of an empty block. 965 * An example empty block might look like the following 966 * <pre> class Foo {}</pre> 967 * 968 * @param ast ast the {@code DetailAST} to test. 969 * @return {@code true} if {@code ast} makes up part of an 970 * empty block contained under a {@code match} token type 971 * node. 972 */ 973 private static boolean isEmptyType(DetailAST ast) { 974 final int type = ast.getType(); 975 final DetailAST nextSibling = ast.getNextSibling(); 976 final DetailAST previousSibling = ast.getPreviousSibling(); 977 return type == TokenTypes.LCURLY 978 && nextSibling.getType() == TokenTypes.RCURLY 979 || previousSibling != null 980 && previousSibling.getType() == TokenTypes.LCURLY; 981 } 982 983 /** 984 * Check if given ast is part of double brace initializer and if it 985 * should omit checking if previous token is separated by whitespace. 986 * 987 * @param ast ast to check 988 * @return true if it should omit checking for previous token, false otherwise 989 */ 990 private static boolean isPartOfDoubleBraceInitializerForPreviousToken(DetailAST ast) { 991 final boolean initializerBeginsAfterClassBegins = 992 ast.getParent().getType() == TokenTypes.INSTANCE_INIT; 993 final boolean classEndsAfterInitializerEnds = ast.getPreviousSibling() != null 994 && ast.getPreviousSibling().getType() == TokenTypes.INSTANCE_INIT; 995 return initializerBeginsAfterClassBegins || classEndsAfterInitializerEnds; 996 } 997 998 /** 999 * Check if given ast is part of double brace initializer and if it 1000 * should omit checking if next token is separated by whitespace. 1001 * See <a href="https://github.com/checkstyle/checkstyle/pull/2845"> 1002 * PR#2845</a> for more information why this function was needed. 1003 * 1004 * @param ast ast to check 1005 * @return true if it should omit checking for next token, false otherwise 1006 */ 1007 private static boolean isPartOfDoubleBraceInitializerForNextToken(DetailAST ast) { 1008 final boolean classBeginBeforeInitializerBegin = ast.getType() == TokenTypes.LCURLY 1009 && ast.getNextSibling().getType() == TokenTypes.INSTANCE_INIT; 1010 final boolean initializerEndsBeforeClassEnds = 1011 ast.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT 1012 && ast.getParent().getParent().getNextSibling().getType() == TokenTypes.RCURLY; 1013 return classBeginBeforeInitializerBegin || initializerEndsBeforeClassEnds; 1014 } 1015 1016}