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 |