| 1 | /////////////////////////////////////////////////////////////////////////////////////////////// | |
| 2 | // checkstyle: Checks Java source code and other text files for adherence to a set of rules. | |
| 3 | // Copyright (C) 2001-2022 the original author or authors. | |
| 4 | // | |
| 5 | // This library is free software; you can redistribute it and/or | |
| 6 | // modify it under the terms of the GNU Lesser General Public | |
| 7 | // License as published by the Free Software Foundation; either | |
| 8 | // version 2.1 of the License, or (at your option) any later version. | |
| 9 | // | |
| 10 | // This library is distributed in the hope that it will be useful, | |
| 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 13 | // Lesser General Public License for more details. | |
| 14 | // | |
| 15 | // You should have received a copy of the GNU Lesser General Public | |
| 16 | // License along with this library; if not, write to the Free Software | |
| 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 18 | /////////////////////////////////////////////////////////////////////////////////////////////// | |
| 19 | ||
| 20 | package com.puppycrawl.tools.checkstyle.checks.blocks; | |
| 21 | ||
| 22 | import java.util.Arrays; | |
| 23 | import java.util.Locale; | |
| 24 | ||
| 25 | import com.puppycrawl.tools.checkstyle.StatelessCheck; | |
| 26 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
| 27 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
| 28 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
| 29 | import com.puppycrawl.tools.checkstyle.utils.CodePointUtil; | |
| 30 | import com.puppycrawl.tools.checkstyle.utils.CommonUtil; | |
| 31 | ||
| 32 | /** | |
| 33 | * <p> | |
| 34 | * Checks for empty blocks. This check does not validate sequential blocks. | |
| 35 | * </p> | |
| 36 | * <p> | |
| 37 | * Sequential blocks won't be checked. Also, no violations for fallthrough: | |
| 38 | * </p> | |
| 39 | * <pre> | |
| 40 | * switch (a) { | |
| 41 | * case 1: // no violation | |
| 42 | * case 2: // no violation | |
| 43 | * case 3: someMethod(); { } // no violation | |
| 44 | * default: break; | |
| 45 | * } | |
| 46 | * </pre> | |
| 47 | * <p> | |
| 48 | * NOTE: This check processes LITERAL_CASE and LITERAL_DEFAULT separately. | |
| 49 | * Verification empty block is done for single nearest {@code case} or {@code default}. | |
| 50 | * </p> | |
| 51 | * <ul> | |
| 52 | * <li> | |
| 53 | * Property {@code option} - specify the policy on block contents. | |
| 54 | * Type is {@code com.puppycrawl.tools.checkstyle.checks.blocks.BlockOption}. | |
| 55 | * Default value is {@code statement}. | |
| 56 | * </li> | |
| 57 | * <li> | |
| 58 | * Property {@code tokens} - tokens to check | |
| 59 | * Type is {@code java.lang.String[]}. | |
| 60 | * Validation type is {@code tokenSet}. | |
| 61 | * Default value is: | |
| 62 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE"> | |
| 63 | * LITERAL_WHILE</a>, | |
| 64 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRY"> | |
| 65 | * LITERAL_TRY</a>, | |
| 66 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FINALLY"> | |
| 67 | * LITERAL_FINALLY</a>, | |
| 68 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO"> | |
| 69 | * LITERAL_DO</a>, | |
| 70 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF"> | |
| 71 | * LITERAL_IF</a>, | |
| 72 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE"> | |
| 73 | * LITERAL_ELSE</a>, | |
| 74 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR"> | |
| 75 | * LITERAL_FOR</a>, | |
| 76 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT"> | |
| 77 | * INSTANCE_INIT</a>, | |
| 78 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STATIC_INIT"> | |
| 79 | * STATIC_INIT</a>, | |
| 80 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH"> | |
| 81 | * LITERAL_SWITCH</a>, | |
| 82 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED"> | |
| 83 | * LITERAL_SYNCHRONIZED</a>. | |
| 84 | * </li> | |
| 85 | * </ul> | |
| 86 | * <p> | |
| 87 | * To configure the check: | |
| 88 | * </p> | |
| 89 | * <pre> | |
| 90 | * <module name="EmptyBlock"/> | |
| 91 | * </pre> | |
| 92 | * <p> | |
| 93 | * Example: | |
| 94 | * </p> | |
| 95 | * <pre> | |
| 96 | * public class Test { | |
| 97 | * private void emptyLoop() { | |
| 98 | * for (int i = 0; i < 10; i++) { // violation | |
| 99 | * } | |
| 100 | * | |
| 101 | * try { // violation | |
| 102 | * | |
| 103 | * } catch (Exception e) { | |
| 104 | * // ignored | |
| 105 | * } | |
| 106 | * } | |
| 107 | * } | |
| 108 | * </pre> | |
| 109 | * <p> | |
| 110 | * To configure the check for the {@code text} policy and only {@code try} blocks: | |
| 111 | * </p> | |
| 112 | * <pre> | |
| 113 | * <module name="EmptyBlock"> | |
| 114 | * <property name="option" value="text"/> | |
| 115 | * <property name="tokens" value="LITERAL_TRY"/> | |
| 116 | * </module> | |
| 117 | * </pre> | |
| 118 | * <p> Example: </p> | |
| 119 | * <pre> | |
| 120 | * public class Test { | |
| 121 | * private void emptyLoop() { | |
| 122 | * for (int i = 0; i < 10; i++) { | |
| 123 | * // ignored | |
| 124 | * } | |
| 125 | * | |
| 126 | * // violation on next line | |
| 127 | * try { | |
| 128 | * | |
| 129 | * } catch (Exception e) { | |
| 130 | * // ignored | |
| 131 | * } | |
| 132 | * } | |
| 133 | * } | |
| 134 | * </pre> | |
| 135 | * <p> | |
| 136 | * To configure the check for default in switch block: | |
| 137 | * </p> | |
| 138 | * <pre> | |
| 139 | * <module name="EmptyBlock"> | |
| 140 | * <property name="tokens" value="LITERAL_DEFAULT"/> | |
| 141 | * </module> | |
| 142 | * </pre> | |
| 143 | * <p> Example: </p> | |
| 144 | * <pre> | |
| 145 | * public class Test { | |
| 146 | * private void test(int a) { | |
| 147 | * switch (a) { | |
| 148 | * case 1: someMethod(); | |
| 149 | * default: // OK, as there is no block | |
| 150 | * } | |
| 151 | * switch (a) { | |
| 152 | * case 1: someMethod(); | |
| 153 | * default: {} // violation | |
| 154 | * } | |
| 155 | * } | |
| 156 | * } | |
| 157 | * </pre> | |
| 158 | * <p> | |
| 159 | * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} | |
| 160 | * </p> | |
| 161 | * <p> | |
| 162 | * Violation Message Keys: | |
| 163 | * </p> | |
| 164 | * <ul> | |
| 165 | * <li> | |
| 166 | * {@code block.empty} | |
| 167 | * </li> | |
| 168 | * <li> | |
| 169 | * {@code block.noStatement} | |
| 170 | * </li> | |
| 171 | * </ul> | |
| 172 | * | |
| 173 | * @since 3.0 | |
| 174 | */ | |
| 175 | @StatelessCheck | |
| 176 | public class EmptyBlockCheck | |
| 177 | extends AbstractCheck { | |
| 178 | ||
| 179 | /** | |
| 180 | * A key is pointing to the warning message text in "messages.properties" | |
| 181 | * file. | |
| 182 | */ | |
| 183 | public static final String MSG_KEY_BLOCK_NO_STATEMENT = "block.noStatement"; | |
| 184 | ||
| 185 | /** | |
| 186 | * A key is pointing to the warning message text in "messages.properties" | |
| 187 | * file. | |
| 188 | */ | |
| 189 | public static final String MSG_KEY_BLOCK_EMPTY = "block.empty"; | |
| 190 | ||
| 191 | /** Specify the policy on block contents. */ | |
| 192 | private BlockOption option = BlockOption.STATEMENT; | |
| 193 | ||
| 194 | /** | |
| 195 | * Setter to specify the policy on block contents. | |
| 196 | * | |
| 197 | * @param optionStr string to decode option from | |
| 198 | * @throws IllegalArgumentException if unable to decode | |
| 199 | */ | |
| 200 | public void setOption(String optionStr) { | |
| 201 | option = BlockOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH)); | |
| 202 | } | |
| 203 | ||
| 204 | @Override | |
| 205 | public int[] getDefaultTokens() { | |
| 206 | return new int[] { | |
| 207 | TokenTypes.LITERAL_WHILE, | |
| 208 | TokenTypes.LITERAL_TRY, | |
| 209 | TokenTypes.LITERAL_FINALLY, | |
| 210 | TokenTypes.LITERAL_DO, | |
| 211 | TokenTypes.LITERAL_IF, | |
| 212 | TokenTypes.LITERAL_ELSE, | |
| 213 | TokenTypes.LITERAL_FOR, | |
| 214 | TokenTypes.INSTANCE_INIT, | |
| 215 | TokenTypes.STATIC_INIT, | |
| 216 | TokenTypes.LITERAL_SWITCH, | |
| 217 | TokenTypes.LITERAL_SYNCHRONIZED, | |
| 218 | }; | |
| 219 | } | |
| 220 | ||
| 221 | @Override | |
| 222 | public int[] getAcceptableTokens() { | |
| 223 | return new int[] { | |
| 224 | TokenTypes.LITERAL_WHILE, | |
| 225 | TokenTypes.LITERAL_TRY, | |
| 226 | TokenTypes.LITERAL_CATCH, | |
| 227 | TokenTypes.LITERAL_FINALLY, | |
| 228 | TokenTypes.LITERAL_DO, | |
| 229 | TokenTypes.LITERAL_IF, | |
| 230 | TokenTypes.LITERAL_ELSE, | |
| 231 | TokenTypes.LITERAL_FOR, | |
| 232 | TokenTypes.INSTANCE_INIT, | |
| 233 | TokenTypes.STATIC_INIT, | |
| 234 | TokenTypes.LITERAL_SWITCH, | |
| 235 | TokenTypes.LITERAL_SYNCHRONIZED, | |
| 236 | TokenTypes.LITERAL_CASE, | |
| 237 | TokenTypes.LITERAL_DEFAULT, | |
| 238 | TokenTypes.ARRAY_INIT, | |
| 239 | }; | |
| 240 | } | |
| 241 | ||
| 242 | @Override | |
| 243 | public int[] getRequiredTokens() { | |
| 244 | return CommonUtil.EMPTY_INT_ARRAY; | |
| 245 | } | |
| 246 | ||
| 247 | @Override | |
| 248 | public void visitToken(DetailAST ast) { | |
| 249 | final DetailAST leftCurly = findLeftCurly(ast); | |
| 250 | if (leftCurly != null) { | |
| 251 | if (option == BlockOption.STATEMENT) { | |
| 252 | final boolean emptyBlock; | |
| 253 | if (leftCurly.getType() == TokenTypes.LCURLY) { | |
| 254 | final DetailAST nextSibling = leftCurly.getNextSibling(); | |
| 255 | emptyBlock = nextSibling.getType() != TokenTypes.CASE_GROUP | |
| 256 | && nextSibling.getType() != TokenTypes.SWITCH_RULE; | |
| 257 | } | |
| 258 | else { | |
| 259 | emptyBlock = leftCurly.getChildCount() <= 1; | |
| 260 | } | |
| 261 | if (emptyBlock) { | |
| 262 | log(leftCurly, | |
| 263 | MSG_KEY_BLOCK_NO_STATEMENT, | |
| 264 | ast.getText()); | |
| 265 | } | |
| 266 | } | |
| 267 | else if (!hasText(leftCurly)) { | |
| 268 | log(leftCurly, | |
| 269 | MSG_KEY_BLOCK_EMPTY, | |
| 270 | ast.getText()); | |
| 271 | } | |
| 272 | ||
| 273 | if (leftCurly == null) { | |
| 274 | if (leftCurly.getType() == TokenTypes.ANNOTATION_DEF) { | |
| 275 | int x = 12 + 123; | |
| 276 | int y = 123 - 4; | |
| 277 |
1
1. visitToken : Replaced integer addition with subtraction → NO_COVERAGE |
int z = y + x; |
| 278 | } | |
| 279 | } | |
| 280 | } | |
| 281 | } | |
| 282 | ||
| 283 | /** | |
| 284 | * Checks if SLIST token contains any text. | |
| 285 | * | |
| 286 | * @param slistAST a {@code DetailAST} value | |
| 287 | * @return whether the SLIST token contains any text. | |
| 288 | */ | |
| 289 | private boolean hasText(final DetailAST slistAST) { | |
| 290 | final DetailAST rightCurly = slistAST.findFirstToken(TokenTypes.RCURLY); | |
| 291 | final DetailAST rcurlyAST; | |
| 292 | ||
| 293 | if (rightCurly == null) { | |
| 294 | rcurlyAST = slistAST.getParent().findFirstToken(TokenTypes.RCURLY); | |
| 295 | } | |
| 296 | else { | |
| 297 | rcurlyAST = rightCurly; | |
| 298 | } | |
| 299 | final int slistLineNo = slistAST.getLineNo(); | |
| 300 | final int slistColNo = slistAST.getColumnNo(); | |
| 301 | final int rcurlyLineNo = rcurlyAST.getLineNo(); | |
| 302 | final int rcurlyColNo = rcurlyAST.getColumnNo(); | |
| 303 | boolean returnValue = false; | |
| 304 | if (slistLineNo == rcurlyLineNo) { | |
| 305 | // Handle braces on the same line | |
| 306 |
2
1. hasText : Replaced integer subtraction with addition → KILLED 2. hasText : Replaced integer addition with subtraction → KILLED |
final int[] txt = Arrays.copyOfRange(getLineCodePoints(slistLineNo - 1), |
| 307 | slistColNo + 1, rcurlyColNo); | |
| 308 | ||
| 309 | if (!CodePointUtil.isBlank(txt)) { | |
| 310 | returnValue = true; | |
| 311 | } | |
| 312 | } | |
| 313 | else { | |
| 314 |
1
1. hasText : Replaced integer subtraction with addition → KILLED |
final int[] codePointsFirstLine = getLineCodePoints(slistLineNo - 1); |
| 315 |
1
1. hasText : Replaced integer addition with subtraction → KILLED |
final int[] firstLine = Arrays.copyOfRange(codePointsFirstLine, |
| 316 | slistColNo + 1, codePointsFirstLine.length); | |
| 317 |
1
1. hasText : Replaced integer subtraction with addition → KILLED |
final int[] codePointsLastLine = getLineCodePoints(rcurlyLineNo - 1); |
| 318 | final int[] lastLine = Arrays.copyOfRange(codePointsLastLine, 0, rcurlyColNo); | |
| 319 | // check if all lines are also only whitespace | |
| 320 | returnValue = !(CodePointUtil.isBlank(firstLine) && CodePointUtil.isBlank(lastLine)) | |
| 321 | || !checkIsAllLinesAreWhitespace(slistLineNo, rcurlyLineNo); | |
| 322 | } | |
| 323 | return returnValue; | |
| 324 | } | |
| 325 | ||
| 326 | /** | |
| 327 | * Checks is all lines from 'lineFrom' to 'lineTo' (exclusive) | |
| 328 | * contain whitespaces only. | |
| 329 | * | |
| 330 | * @param lineFrom | |
| 331 | * check from this line number | |
| 332 | * @param lineTo | |
| 333 | * check to this line numbers | |
| 334 | * @return true if lines contain only whitespaces | |
| 335 | */ | |
| 336 | private boolean checkIsAllLinesAreWhitespace(int lineFrom, int lineTo) { | |
| 337 | boolean result = true; | |
| 338 |
1
1. checkIsAllLinesAreWhitespace : Replaced integer subtraction with addition → KILLED |
for (int i = lineFrom; i < lineTo - 1; i++) { |
| 339 | if (!CodePointUtil.isBlank(getLineCodePoints(i))) { | |
| 340 | result = false; | |
| 341 | break; | |
| 342 | } | |
| 343 | } | |
| 344 | return result; | |
| 345 | } | |
| 346 | ||
| 347 | /** | |
| 348 | * Calculates the left curly corresponding to the block to be checked. | |
| 349 | * | |
| 350 | * @param ast a {@code DetailAST} value | |
| 351 | * @return the left curly corresponding to the block to be checked | |
| 352 | */ | |
| 353 | private static DetailAST findLeftCurly(DetailAST ast) { | |
| 354 | final DetailAST leftCurly; | |
| 355 | final DetailAST slistAST = ast.findFirstToken(TokenTypes.SLIST); | |
| 356 | if ((ast.getType() == TokenTypes.LITERAL_CASE | |
| 357 | || ast.getType() == TokenTypes.LITERAL_DEFAULT) | |
| 358 | && ast.getNextSibling() != null | |
| 359 | && ast.getNextSibling().getFirstChild() != null | |
| 360 | && ast.getNextSibling().getFirstChild().getType() == TokenTypes.SLIST) { | |
| 361 | leftCurly = ast.getNextSibling().getFirstChild(); | |
| 362 | } | |
| 363 | else if (slistAST == null) { | |
| 364 | leftCurly = ast.findFirstToken(TokenTypes.LCURLY); | |
| 365 | } | |
| 366 | else { | |
| 367 | leftCurly = slistAST; | |
| 368 | } | |
| 369 | return leftCurly; | |
| 370 | } | |
| 371 | ||
| 372 | } | |
Mutations | ||
| 277 |
1.1 |
|
| 306 |
1.1 2.2 |
|
| 314 |
1.1 |
|
| 315 |
1.1 |
|
| 317 |
1.1 |
|
| 338 |
1.1 |