1 | //////////////////////////////////////////////////////////////////////////////// | |
2 | // checkstyle: Checks Java source code for adherence to a set of rules. | |
3 | // Copyright (C) 2001-2018 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.coding; | |
21 | ||
22 | import java.util.AbstractMap.SimpleEntry; | |
23 | import java.util.ArrayList; | |
24 | import java.util.List; | |
25 | import java.util.Map.Entry; | |
26 | import java.util.regex.Matcher; | |
27 | import java.util.regex.Pattern; | |
28 | ||
29 | import antlr.collections.ASTEnumeration; | |
30 | import com.puppycrawl.tools.checkstyle.StatelessCheck; | |
31 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
32 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
33 | import com.puppycrawl.tools.checkstyle.api.FullIdent; | |
34 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
35 | ||
36 | /** | |
37 | * <p> | |
38 | * Checks the distance between declaration of variable and its first usage. | |
39 | * </p> | |
40 | * Example #1: | |
41 | * <pre> | |
42 | * {@code int count; | |
43 | * a = a + b; | |
44 | * b = a + a; | |
45 | * count = b; // DECLARATION OF VARIABLE 'count' | |
46 | * // SHOULD BE HERE (distance = 3)} | |
47 | * </pre> | |
48 | * Example #2: | |
49 | * <pre> | |
50 | * {@code int count; | |
51 | * { | |
52 | * a = a + b; | |
53 | * count = b; // DECLARATION OF VARIABLE 'count' | |
54 | * // SHOULD BE HERE (distance = 2) | |
55 | * }} | |
56 | * </pre> | |
57 | * | |
58 | * <p> | |
59 | * Check can detect a block of initialization methods. If a variable is used in | |
60 | * such a block and there is no other statements after this variable then distance=1. | |
61 | * </p> | |
62 | * | |
63 | * <p><b>Case #1:</b> | |
64 | * <pre> | |
65 | * int <b>minutes</b> = 5; | |
66 | * Calendar cal = Calendar.getInstance(); | |
67 | * cal.setTimeInMillis(timeNow); | |
68 | * cal.set(Calendar.SECOND, 0); | |
69 | * cal.set(Calendar.MILLISECOND, 0); | |
70 | * cal.set(Calendar.HOUR_OF_DAY, hh); | |
71 | * cal.set(Calendar.MINUTE, <b>minutes</b>); | |
72 | * | |
73 | * The distance for the variable <b>minutes</b> is 1 even | |
74 | * though this variable is used in the fifth method's call. | |
75 | * </pre> | |
76 | * | |
77 | * <p><b>Case #2:</b> | |
78 | * <pre> | |
79 | * int <b>minutes</b> = 5; | |
80 | * Calendar cal = Calendar.getInstance(); | |
81 | * cal.setTimeInMillis(timeNow); | |
82 | * cal.set(Calendar.SECOND, 0); | |
83 | * cal.set(Calendar.MILLISECOND, 0); | |
84 | * <i>System.out.println(cal);</i> | |
85 | * cal.set(Calendar.HOUR_OF_DAY, hh); | |
86 | * cal.set(Calendar.MINUTE, <b>minutes</b>); | |
87 | * | |
88 | * The distance for the variable <b>minutes</b> is 6 because there is one more expression | |
89 | * (except the initialization block) between the declaration of this variable and its usage. | |
90 | * </pre> | |
91 | * | |
92 | * <p>There are several additional options to configure the check: | |
93 | * <pre> | |
94 | * 1. allowedDistance - allows to set a distance | |
95 | * between declaration of variable and its first usage. | |
96 | * 2. ignoreVariablePattern - allows to set a RegEx pattern for | |
97 | * ignoring the distance calculation for variables listed in this pattern. | |
98 | * 3. validateBetweenScopes - allows to calculate the distance between | |
99 | * declaration of variable and its first usage in the different scopes. | |
100 | * 4. ignoreFinal - allows to ignore variables with a 'final' modifier. | |
101 | * </pre> | |
102 | * ATTENTION!! (Not supported cases) | |
103 | * <pre> | |
104 | * Case #1: | |
105 | * {@code { | |
106 | * int c; | |
107 | * int a = 3; | |
108 | * int b = 2; | |
109 | * { | |
110 | * a = a + b; | |
111 | * c = b; | |
112 | * } | |
113 | * }} | |
114 | * | |
115 | * Distance for variable 'a' = 1; | |
116 | * Distance for variable 'b' = 1; | |
117 | * Distance for variable 'c' = 2. | |
118 | * </pre> | |
119 | * As distance by default is 1 the Check doesn't raise warning for variables 'a' | |
120 | * and 'b' to move them into the block. | |
121 | * <pre> | |
122 | * Case #2: | |
123 | * {@code int sum = 0; | |
124 | * for (int i = 0; i < 20; i++) { | |
125 | * a++; | |
126 | * b--; | |
127 | * sum++; | |
128 | * if (sum > 10) { | |
129 | * res = true; | |
130 | * } | |
131 | * }} | |
132 | * Distance for variable 'sum' = 3. | |
133 | * </pre> | |
134 | * <p> | |
135 | * As the distance is more then the default one, the Check raises warning for variable | |
136 | * 'sum' to move it into the 'for(...)' block. But there is situation when | |
137 | * variable 'sum' hasn't to be 0 within each iteration. So, to avoid such | |
138 | * warnings you can use Suppression Filter, provided by Checkstyle, for the | |
139 | * whole class. | |
140 | * </p> | |
141 | * | |
142 | * <p> | |
143 | * An example how to configure this Check: | |
144 | * </p> | |
145 | * <pre> | |
146 | * <module name="VariableDeclarationUsageDistance"/> | |
147 | * </pre> | |
148 | * <p> | |
149 | * An example of how to configure this Check: | |
150 | * - to set the allowed distance to 4; | |
151 | * - to ignore variables with prefix '^temp'; | |
152 | * - to force the validation between scopes; | |
153 | * - to check the final variables; | |
154 | * </p> | |
155 | * <pre> | |
156 | * <module name="VariableDeclarationUsageDistance"> | |
157 | * <property name="allowedDistance" value="4"/> | |
158 | * <property name="ignoreVariablePattern" value="^temp.*"/> | |
159 | * <property name="validateBetweenScopes" value="true"/> | |
160 | * <property name="ignoreFinal" value="false"/> | |
161 | * </module> | |
162 | * </pre> | |
163 | * | |
164 | * @author <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko</a> | |
165 | * @author <a href="mailto:barataliba@gmail.com">Baratali Izmailov</a> | |
166 | */ | |
167 | @StatelessCheck | |
168 | public class VariableDeclarationUsageDistanceCheck extends AbstractCheck { | |
169 | ||
170 | /** | |
171 | * Warning message key. | |
172 | */ | |
173 | public static final String MSG_KEY = "variable.declaration.usage.distance"; | |
174 | ||
175 | /** | |
176 | * Warning message key. | |
177 | */ | |
178 | public static final String MSG_KEY_EXT = "variable.declaration.usage.distance.extend"; | |
179 | ||
180 | /** | |
181 | * Default value of distance between declaration of variable and its first | |
182 | * usage. | |
183 | */ | |
184 | private static final int DEFAULT_DISTANCE = 3; | |
185 | ||
186 | /** Allowed distance between declaration of variable and its first usage. */ | |
187 | private int allowedDistance = DEFAULT_DISTANCE; | |
188 | ||
189 | /** | |
190 | * RegExp pattern to ignore distance calculation for variables listed in | |
191 | * this pattern. | |
192 | */ | |
193 | private Pattern ignoreVariablePattern = Pattern.compile(""); | |
194 | ||
195 | /** | |
196 | * Allows to calculate distance between declaration of variable and its | |
197 | * first usage in different scopes. | |
198 | */ | |
199 | private boolean validateBetweenScopes; | |
200 | ||
201 | /** Allows to ignore variables with 'final' modifier. */ | |
202 | private boolean ignoreFinal = true; | |
203 | ||
204 | /** | |
205 | * Sets an allowed distance between declaration of variable and its first | |
206 | * usage. | |
207 | * @param allowedDistance | |
208 | * Allowed distance between declaration of variable and its first | |
209 | * usage. | |
210 | */ | |
211 | public void setAllowedDistance(int allowedDistance) { | |
212 | this.allowedDistance = allowedDistance; | |
213 | } | |
214 | ||
215 | /** | |
216 | * Sets RegExp pattern to ignore distance calculation for variables listed in this pattern. | |
217 | * @param pattern a pattern. | |
218 | */ | |
219 | public void setIgnoreVariablePattern(Pattern pattern) { | |
220 | ignoreVariablePattern = pattern; | |
221 | } | |
222 | ||
223 | /** | |
224 | * Sets option which allows to calculate distance between declaration of | |
225 | * variable and its first usage in different scopes. | |
226 | * @param validateBetweenScopes | |
227 | * Defines if allow to calculate distance between declaration of | |
228 | * variable and its first usage in different scopes or not. | |
229 | */ | |
230 | public void setValidateBetweenScopes(boolean validateBetweenScopes) { | |
231 | this.validateBetweenScopes = validateBetweenScopes; | |
232 | } | |
233 | ||
234 | /** | |
235 | * Sets ignore option for variables with 'final' modifier. | |
236 | * @param ignoreFinal | |
237 | * Defines if ignore variables with 'final' modifier or not. | |
238 | */ | |
239 | public void setIgnoreFinal(boolean ignoreFinal) { | |
240 | this.ignoreFinal = ignoreFinal; | |
241 | } | |
242 | ||
243 | @Override | |
244 | public int[] getDefaultTokens() { | |
245 |
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getRequiredTokens(); |
246 | } | |
247 | ||
248 | @Override | |
249 | public int[] getAcceptableTokens() { | |
250 |
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getRequiredTokens(); |
251 | } | |
252 | ||
253 | @Override | |
254 | public int[] getRequiredTokens() { | |
255 |
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] {TokenTypes.VARIABLE_DEF}; |
256 | } | |
257 | ||
258 | @Override | |
259 | public void visitToken(DetailAST ast) { | |
260 | final int parentType = ast.getParent().getType(); | |
261 | final DetailAST modifiers = ast.getFirstChild(); | |
262 | ||
263 |
2
1. visitToken : negated conditional → KILLED 2. visitToken : negated conditional → KILLED |
if (parentType != TokenTypes.OBJBLOCK |
264 |
1
1. visitToken : negated conditional → KILLED |
&& (!ignoreFinal || modifiers.findFirstToken(TokenTypes.FINAL) == null)) { |
265 | final DetailAST variable = ast.findFirstToken(TokenTypes.IDENT); | |
266 | ||
267 |
1
1. visitToken : negated conditional → KILLED |
if (!isVariableMatchesIgnorePattern(variable.getText())) { |
268 | final DetailAST semicolonAst = ast.getNextSibling(); | |
269 | final Entry<DetailAST, Integer> entry; | |
270 |
1
1. visitToken : negated conditional → KILLED |
if (validateBetweenScopes) { |
271 | entry = calculateDistanceBetweenScopes(semicolonAst, variable); | |
272 | } | |
273 | else { | |
274 | entry = calculateDistanceInSingleScope(semicolonAst, variable); | |
275 | } | |
276 | final DetailAST variableUsageAst = entry.getKey(); | |
277 | final int dist = entry.getValue(); | |
278 |
2
1. visitToken : changed conditional boundary → KILLED 2. visitToken : negated conditional → KILLED |
if (dist > allowedDistance |
279 |
1
1. visitToken : negated conditional → KILLED |
&& !isInitializationSequence(variableUsageAst, variable.getText())) { |
280 |
1
1. visitToken : negated conditional → KILLED |
if (ignoreFinal) { |
281 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::log → KILLED |
log(variable.getLineNo(), |
282 | MSG_KEY_EXT, variable.getText(), dist, allowedDistance); | |
283 | } | |
284 | else { | |
285 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::log → KILLED |
log(variable.getLineNo(), |
286 | MSG_KEY, variable.getText(), dist, allowedDistance); | |
287 | } | |
288 | } | |
289 | } | |
290 | } | |
291 | } | |
292 | ||
293 | /** | |
294 | * Get name of instance whose method is called. | |
295 | * @param methodCallAst | |
296 | * DetailAST of METHOD_CALL. | |
297 | * @return name of instance. | |
298 | */ | |
299 | private static String getInstanceName(DetailAST methodCallAst) { | |
300 | final String methodCallName = | |
301 | FullIdent.createFullIdentBelow(methodCallAst).getText(); | |
302 | final int lastDotIndex = methodCallName.lastIndexOf('.'); | |
303 | String instanceName = ""; | |
304 |
1
1. getInstanceName : negated conditional → KILLED |
if (lastDotIndex != -1) { |
305 | instanceName = methodCallName.substring(0, lastDotIndex); | |
306 | } | |
307 |
1
1. getInstanceName : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::getInstanceName to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return instanceName; |
308 | } | |
309 | ||
310 | /** | |
311 | * Processes statements until usage of variable to detect sequence of | |
312 | * initialization methods. | |
313 | * @param variableUsageAst | |
314 | * DetailAST of expression that uses variable named variableName. | |
315 | * @param variableName | |
316 | * name of considered variable. | |
317 | * @return true if statements between declaration and usage of variable are | |
318 | * initialization methods. | |
319 | */ | |
320 | private static boolean isInitializationSequence( | |
321 | DetailAST variableUsageAst, String variableName) { | |
322 | boolean result = true; | |
323 | boolean isUsedVariableDeclarationFound = false; | |
324 | DetailAST currentSiblingAst = variableUsageAst; | |
325 | String initInstanceName = ""; | |
326 | ||
327 |
3
1. isInitializationSequence : negated conditional → KILLED 2. isInitializationSequence : negated conditional → KILLED 3. isInitializationSequence : negated conditional → KILLED |
while (result |
328 | && !isUsedVariableDeclarationFound | |
329 | && currentSiblingAst != null) { | |
330 | switch (currentSiblingAst.getType()) { | |
331 | case TokenTypes.EXPR: | |
332 | final DetailAST methodCallAst = currentSiblingAst.getFirstChild(); | |
333 | ||
334 |
1
1. isInitializationSequence : negated conditional → KILLED |
if (methodCallAst.getType() == TokenTypes.METHOD_CALL) { |
335 | final String instanceName = | |
336 | getInstanceName(methodCallAst); | |
337 | // method is called without instance | |
338 |
1
1. isInitializationSequence : negated conditional → KILLED |
if (instanceName.isEmpty()) { |
339 | result = false; | |
340 | } | |
341 | // differs from previous instance | |
342 |
1
1. isInitializationSequence : negated conditional → KILLED |
else if (!instanceName.equals(initInstanceName)) { |
343 |
1
1. isInitializationSequence : negated conditional → KILLED |
if (initInstanceName.isEmpty()) { |
344 | initInstanceName = instanceName; | |
345 | } | |
346 | else { | |
347 | result = false; | |
348 | } | |
349 | } | |
350 | } | |
351 | else { | |
352 | // is not method call | |
353 | result = false; | |
354 | } | |
355 | break; | |
356 | ||
357 | case TokenTypes.VARIABLE_DEF: | |
358 | final String currentVariableName = currentSiblingAst | |
359 | .findFirstToken(TokenTypes.IDENT).getText(); | |
360 | isUsedVariableDeclarationFound = variableName.equals(currentVariableName); | |
361 | break; | |
362 | ||
363 | case TokenTypes.SEMI: | |
364 | break; | |
365 | ||
366 | default: | |
367 | result = false; | |
368 | } | |
369 | ||
370 | currentSiblingAst = currentSiblingAst.getPreviousSibling(); | |
371 | } | |
372 | ||
373 |
1
1. isInitializationSequence : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
374 | } | |
375 | ||
376 | /** | |
377 | * Calculates distance between declaration of variable and its first usage | |
378 | * in single scope. | |
379 | * @param semicolonAst | |
380 | * Regular node of Ast which is checked for content of checking | |
381 | * variable. | |
382 | * @param variableIdentAst | |
383 | * Variable which distance is calculated for. | |
384 | * @return entry which contains expression with variable usage and distance. | |
385 | */ | |
386 | private static Entry<DetailAST, Integer> calculateDistanceInSingleScope( | |
387 | DetailAST semicolonAst, DetailAST variableIdentAst) { | |
388 | int dist = 0; | |
389 | boolean firstUsageFound = false; | |
390 | DetailAST currentAst = semicolonAst; | |
391 | DetailAST variableUsageAst = null; | |
392 | ||
393 |
2
1. calculateDistanceInSingleScope : negated conditional → KILLED 2. calculateDistanceInSingleScope : negated conditional → KILLED |
while (!firstUsageFound && currentAst != null |
394 |
1
1. calculateDistanceInSingleScope : negated conditional → KILLED |
&& currentAst.getType() != TokenTypes.RCURLY) { |
395 |
1
1. calculateDistanceInSingleScope : negated conditional → KILLED |
if (currentAst.getFirstChild() != null) { |
396 |
1
1. calculateDistanceInSingleScope : negated conditional → KILLED |
if (isChild(currentAst, variableIdentAst)) { |
397 | dist = getDistToVariableUsageInChildNode(currentAst, variableIdentAst, dist); | |
398 | variableUsageAst = currentAst; | |
399 | firstUsageFound = true; | |
400 | } | |
401 |
1
1. calculateDistanceInSingleScope : negated conditional → KILLED |
else if (currentAst.getType() != TokenTypes.VARIABLE_DEF) { |
402 |
1
1. calculateDistanceInSingleScope : Changed increment from 1 to -1 → KILLED |
dist++; |
403 | } | |
404 | } | |
405 | currentAst = currentAst.getNextSibling(); | |
406 | } | |
407 | ||
408 | // If variable wasn't used after its declaration, distance is 0. | |
409 |
1
1. calculateDistanceInSingleScope : negated conditional → KILLED |
if (!firstUsageFound) { |
410 | dist = 0; | |
411 | } | |
412 | ||
413 |
1
1. calculateDistanceInSingleScope : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::calculateDistanceInSingleScope to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new SimpleEntry<>(variableUsageAst, dist); |
414 | } | |
415 | ||
416 | /** | |
417 | * Returns the distance to variable usage for in the child node. | |
418 | * @param childNode child node. | |
419 | * @param varIdent variable variable identifier. | |
420 | * @param currentDistToVarUsage current distance to the variable usage. | |
421 | * @return the distance to variable usage for in the child node. | |
422 | */ | |
423 | private static int getDistToVariableUsageInChildNode(DetailAST childNode, DetailAST varIdent, | |
424 | int currentDistToVarUsage) { | |
425 | DetailAST examineNode = childNode; | |
426 |
1
1. getDistToVariableUsageInChildNode : negated conditional → KILLED |
if (examineNode.getType() == TokenTypes.LABELED_STAT) { |
427 | examineNode = examineNode.getFirstChild().getNextSibling(); | |
428 | } | |
429 | ||
430 | int resultDist = currentDistToVarUsage; | |
431 | switch (examineNode.getType()) { | |
432 | case TokenTypes.VARIABLE_DEF: | |
433 |
1
1. getDistToVariableUsageInChildNode : Changed increment from 1 to -1 → KILLED |
resultDist++; |
434 | break; | |
435 | case TokenTypes.SLIST: | |
436 | resultDist = 0; | |
437 | break; | |
438 | case TokenTypes.LITERAL_FOR: | |
439 | case TokenTypes.LITERAL_WHILE: | |
440 | case TokenTypes.LITERAL_DO: | |
441 | case TokenTypes.LITERAL_IF: | |
442 | case TokenTypes.LITERAL_SWITCH: | |
443 |
1
1. getDistToVariableUsageInChildNode : negated conditional → KILLED |
if (isVariableInOperatorExpr(examineNode, varIdent)) { |
444 |
1
1. getDistToVariableUsageInChildNode : Changed increment from 1 to -1 → KILLED |
resultDist++; |
445 | } | |
446 | else { | |
447 | // variable usage is in inner scope | |
448 | // reset counters, because we can't determine distance | |
449 | resultDist = 0; | |
450 | } | |
451 | break; | |
452 | default: | |
453 |
1
1. getDistToVariableUsageInChildNode : negated conditional → KILLED |
if (examineNode.findFirstToken(TokenTypes.SLIST) == null) { |
454 |
1
1. getDistToVariableUsageInChildNode : Changed increment from 1 to -1 → KILLED |
resultDist++; |
455 | } | |
456 | else { | |
457 | resultDist = 0; | |
458 | } | |
459 | } | |
460 |
1
1. getDistToVariableUsageInChildNode : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return resultDist; |
461 | } | |
462 | ||
463 | /** | |
464 | * Calculates distance between declaration of variable and its first usage | |
465 | * in multiple scopes. | |
466 | * @param ast | |
467 | * Regular node of Ast which is checked for content of checking | |
468 | * variable. | |
469 | * @param variable | |
470 | * Variable which distance is calculated for. | |
471 | * @return entry which contains expression with variable usage and distance. | |
472 | */ | |
473 | private static Entry<DetailAST, Integer> calculateDistanceBetweenScopes( | |
474 | DetailAST ast, DetailAST variable) { | |
475 | int dist = 0; | |
476 | DetailAST currentScopeAst = ast; | |
477 | DetailAST variableUsageAst = null; | |
478 |
1
1. calculateDistanceBetweenScopes : negated conditional → TIMED_OUT |
while (currentScopeAst != null) { |
479 | final Entry<List<DetailAST>, Integer> searchResult = | |
480 | searchVariableUsageExpressions(variable, currentScopeAst); | |
481 | ||
482 | currentScopeAst = null; | |
483 | ||
484 | final List<DetailAST> variableUsageExpressions = searchResult.getKey(); | |
485 |
1
1. calculateDistanceBetweenScopes : Replaced integer addition with subtraction → KILLED |
dist += searchResult.getValue(); |
486 | ||
487 | // If variable usage exists in a single scope, then look into | |
488 | // this scope and count distance until variable usage. | |
489 |
1
1. calculateDistanceBetweenScopes : negated conditional → KILLED |
if (variableUsageExpressions.size() == 1) { |
490 | final DetailAST blockWithVariableUsage = variableUsageExpressions | |
491 | .get(0); | |
492 | DetailAST exprWithVariableUsage = null; | |
493 | switch (blockWithVariableUsage.getType()) { | |
494 | case TokenTypes.VARIABLE_DEF: | |
495 | case TokenTypes.EXPR: | |
496 |
1
1. calculateDistanceBetweenScopes : Changed increment from 1 to -1 → KILLED |
dist++; |
497 | break; | |
498 | case TokenTypes.LITERAL_FOR: | |
499 | case TokenTypes.LITERAL_WHILE: | |
500 | case TokenTypes.LITERAL_DO: | |
501 | exprWithVariableUsage = getFirstNodeInsideForWhileDoWhileBlocks( | |
502 | blockWithVariableUsage, variable); | |
503 | break; | |
504 | case TokenTypes.LITERAL_IF: | |
505 | exprWithVariableUsage = getFirstNodeInsideIfBlock( | |
506 | blockWithVariableUsage, variable); | |
507 | break; | |
508 | case TokenTypes.LITERAL_SWITCH: | |
509 | exprWithVariableUsage = getFirstNodeInsideSwitchBlock( | |
510 | blockWithVariableUsage, variable); | |
511 | break; | |
512 | case TokenTypes.LITERAL_TRY: | |
513 | exprWithVariableUsage = | |
514 | getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage, | |
515 | variable); | |
516 | break; | |
517 | default: | |
518 | exprWithVariableUsage = blockWithVariableUsage.getFirstChild(); | |
519 | } | |
520 | currentScopeAst = exprWithVariableUsage; | |
521 |
1
1. calculateDistanceBetweenScopes : negated conditional → KILLED |
if (exprWithVariableUsage == null) { |
522 | variableUsageAst = blockWithVariableUsage; | |
523 | } | |
524 | else { | |
525 | variableUsageAst = exprWithVariableUsage; | |
526 | } | |
527 | } | |
528 | ||
529 | // If there's no any variable usage, then distance = 0. | |
530 |
1
1. calculateDistanceBetweenScopes : negated conditional → KILLED |
else if (variableUsageExpressions.isEmpty()) { |
531 | variableUsageAst = null; | |
532 | } | |
533 | // If variable usage exists in different scopes, then distance = | |
534 | // distance until variable first usage. | |
535 | else { | |
536 |
1
1. calculateDistanceBetweenScopes : Changed increment from 1 to -1 → KILLED |
dist++; |
537 | variableUsageAst = variableUsageExpressions.get(0); | |
538 | } | |
539 | } | |
540 |
1
1. calculateDistanceBetweenScopes : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::calculateDistanceBetweenScopes to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new SimpleEntry<>(variableUsageAst, dist); |
541 | } | |
542 | ||
543 | /** | |
544 | * Searches variable usages starting from specified statement. | |
545 | * @param variableAst Variable that is used. | |
546 | * @param statementAst DetailAST to start searching from. | |
547 | * @return entry which contains list with found expressions that use the variable | |
548 | * and distance from specified statement to first found expression. | |
549 | */ | |
550 | private static Entry<List<DetailAST>, Integer> | |
551 | searchVariableUsageExpressions(final DetailAST variableAst, final DetailAST statementAst) { | |
552 | final List<DetailAST> variableUsageExpressions = new ArrayList<>(); | |
553 | int distance = 0; | |
554 | DetailAST currentStatementAst = statementAst; | |
555 |
1
1. searchVariableUsageExpressions : negated conditional → KILLED |
while (currentStatementAst != null |
556 |
1
1. searchVariableUsageExpressions : negated conditional → KILLED |
&& currentStatementAst.getType() != TokenTypes.RCURLY) { |
557 |
1
1. searchVariableUsageExpressions : negated conditional → KILLED |
if (currentStatementAst.getFirstChild() != null) { |
558 |
1
1. searchVariableUsageExpressions : negated conditional → KILLED |
if (isChild(currentStatementAst, variableAst)) { |
559 | variableUsageExpressions.add(currentStatementAst); | |
560 | } | |
561 | // If expression doesn't contain variable and this variable | |
562 | // hasn't been met yet, than distance + 1. | |
563 |
1
1. searchVariableUsageExpressions : negated conditional → KILLED |
else if (variableUsageExpressions.isEmpty() |
564 |
1
1. searchVariableUsageExpressions : negated conditional → KILLED |
&& currentStatementAst.getType() != TokenTypes.VARIABLE_DEF) { |
565 |
1
1. searchVariableUsageExpressions : Changed increment from 1 to -1 → KILLED |
distance++; |
566 | } | |
567 | } | |
568 | currentStatementAst = currentStatementAst.getNextSibling(); | |
569 | } | |
570 |
1
1. searchVariableUsageExpressions : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::searchVariableUsageExpressions to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new SimpleEntry<>(variableUsageExpressions, distance); |
571 | } | |
572 | ||
573 | /** | |
574 | * Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable | |
575 | * usage is met only inside the block (not in its declaration!). | |
576 | * @param block | |
577 | * Ast node represents FOR, WHILE or DO-WHILE block. | |
578 | * @param variable | |
579 | * Variable which is checked for content in block. | |
580 | * @return If variable usage is met only inside the block | |
581 | * (not in its declaration!) than return the first Ast node | |
582 | * of this block, otherwise - null. | |
583 | */ | |
584 | private static DetailAST getFirstNodeInsideForWhileDoWhileBlocks( | |
585 | DetailAST block, DetailAST variable) { | |
586 | DetailAST firstNodeInsideBlock = null; | |
587 | ||
588 |
1
1. getFirstNodeInsideForWhileDoWhileBlocks : negated conditional → KILLED |
if (!isVariableInOperatorExpr(block, variable)) { |
589 | final DetailAST currentNode; | |
590 | ||
591 | // Find currentNode for DO-WHILE block. | |
592 |
1
1. getFirstNodeInsideForWhileDoWhileBlocks : negated conditional → KILLED |
if (block.getType() == TokenTypes.LITERAL_DO) { |
593 | currentNode = block.getFirstChild(); | |
594 | } | |
595 | // Find currentNode for FOR or WHILE block. | |
596 | else { | |
597 | // Looking for RPAREN ( ')' ) token to mark the end of operator | |
598 | // expression. | |
599 | currentNode = block.findFirstToken(TokenTypes.RPAREN).getNextSibling(); | |
600 | } | |
601 | ||
602 | final int currentNodeType = currentNode.getType(); | |
603 | ||
604 |
1
1. getFirstNodeInsideForWhileDoWhileBlocks : negated conditional → KILLED |
if (currentNodeType == TokenTypes.SLIST) { |
605 | firstNodeInsideBlock = currentNode.getFirstChild(); | |
606 | } | |
607 |
1
1. getFirstNodeInsideForWhileDoWhileBlocks : negated conditional → KILLED |
else if (currentNodeType != TokenTypes.EXPR) { |
608 | firstNodeInsideBlock = currentNode; | |
609 | } | |
610 | } | |
611 | ||
612 |
1
1. getFirstNodeInsideForWhileDoWhileBlocks : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::getFirstNodeInsideForWhileDoWhileBlocks to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return firstNodeInsideBlock; |
613 | } | |
614 | ||
615 | /** | |
616 | * Gets first Ast node inside IF block if variable usage is met | |
617 | * only inside the block (not in its declaration!). | |
618 | * @param block | |
619 | * Ast node represents IF block. | |
620 | * @param variable | |
621 | * Variable which is checked for content in block. | |
622 | * @return If variable usage is met only inside the block | |
623 | * (not in its declaration!) than return the first Ast node | |
624 | * of this block, otherwise - null. | |
625 | */ | |
626 | private static DetailAST getFirstNodeInsideIfBlock( | |
627 | DetailAST block, DetailAST variable) { | |
628 | DetailAST firstNodeInsideBlock = null; | |
629 | ||
630 |
1
1. getFirstNodeInsideIfBlock : negated conditional → KILLED |
if (!isVariableInOperatorExpr(block, variable)) { |
631 | DetailAST currentNode = block.getLastChild(); | |
632 | final List<DetailAST> variableUsageExpressions = | |
633 | new ArrayList<>(); | |
634 | ||
635 |
1
1. getFirstNodeInsideIfBlock : negated conditional → KILLED |
while (currentNode != null |
636 |
1
1. getFirstNodeInsideIfBlock : negated conditional → KILLED |
&& currentNode.getType() == TokenTypes.LITERAL_ELSE) { |
637 | final DetailAST previousNode = | |
638 | currentNode.getPreviousSibling(); | |
639 | ||
640 | // Checking variable usage inside IF block. | |
641 |
1
1. getFirstNodeInsideIfBlock : negated conditional → KILLED |
if (isChild(previousNode, variable)) { |
642 | variableUsageExpressions.add(previousNode); | |
643 | } | |
644 | ||
645 | // Looking into ELSE block, get its first child and analyze it. | |
646 | currentNode = currentNode.getFirstChild(); | |
647 | ||
648 |
1
1. getFirstNodeInsideIfBlock : negated conditional → SURVIVED |
if (currentNode.getType() == TokenTypes.LITERAL_IF) { |
649 | currentNode = currentNode.getLastChild(); | |
650 | } | |
651 |
1
1. getFirstNodeInsideIfBlock : negated conditional → KILLED |
else if (isChild(currentNode, variable)) { |
652 | variableUsageExpressions.add(currentNode); | |
653 | currentNode = null; | |
654 | } | |
655 | } | |
656 | ||
657 | // If IF block doesn't include ELSE than analyze variable usage | |
658 | // only inside IF block. | |
659 |
1
1. getFirstNodeInsideIfBlock : negated conditional → KILLED |
if (currentNode != null |
660 |
1
1. getFirstNodeInsideIfBlock : negated conditional → KILLED |
&& isChild(currentNode, variable)) { |
661 | variableUsageExpressions.add(currentNode); | |
662 | } | |
663 | ||
664 | // If variable usage exists in several related blocks, then | |
665 | // firstNodeInsideBlock = null, otherwise if variable usage exists | |
666 | // only inside one block, then get node from | |
667 | // variableUsageExpressions. | |
668 |
1
1. getFirstNodeInsideIfBlock : negated conditional → KILLED |
if (variableUsageExpressions.size() == 1) { |
669 | firstNodeInsideBlock = variableUsageExpressions.get(0); | |
670 | } | |
671 | } | |
672 | ||
673 |
1
1. getFirstNodeInsideIfBlock : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::getFirstNodeInsideIfBlock to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return firstNodeInsideBlock; |
674 | } | |
675 | ||
676 | /** | |
677 | * Gets first Ast node inside SWITCH block if variable usage is met | |
678 | * only inside the block (not in its declaration!). | |
679 | * @param block | |
680 | * Ast node represents SWITCH block. | |
681 | * @param variable | |
682 | * Variable which is checked for content in block. | |
683 | * @return If variable usage is met only inside the block | |
684 | * (not in its declaration!) than return the first Ast node | |
685 | * of this block, otherwise - null. | |
686 | */ | |
687 | private static DetailAST getFirstNodeInsideSwitchBlock( | |
688 | DetailAST block, DetailAST variable) { | |
689 | DetailAST currentNode = block | |
690 | .findFirstToken(TokenTypes.CASE_GROUP); | |
691 | final List<DetailAST> variableUsageExpressions = | |
692 | new ArrayList<>(); | |
693 | ||
694 | // Checking variable usage inside all CASE blocks. | |
695 |
1
1. getFirstNodeInsideSwitchBlock : negated conditional → KILLED |
while (currentNode.getType() == TokenTypes.CASE_GROUP) { |
696 | final DetailAST lastNodeInCaseGroup = | |
697 | currentNode.getLastChild(); | |
698 | ||
699 |
1
1. getFirstNodeInsideSwitchBlock : negated conditional → KILLED |
if (isChild(lastNodeInCaseGroup, variable)) { |
700 | variableUsageExpressions.add(lastNodeInCaseGroup); | |
701 | } | |
702 | currentNode = currentNode.getNextSibling(); | |
703 | } | |
704 | ||
705 | // If variable usage exists in several related blocks, then | |
706 | // firstNodeInsideBlock = null, otherwise if variable usage exists | |
707 | // only inside one block, then get node from | |
708 | // variableUsageExpressions. | |
709 | DetailAST firstNodeInsideBlock = null; | |
710 |
1
1. getFirstNodeInsideSwitchBlock : negated conditional → KILLED |
if (variableUsageExpressions.size() == 1) { |
711 | firstNodeInsideBlock = variableUsageExpressions.get(0); | |
712 | } | |
713 | ||
714 |
1
1. getFirstNodeInsideSwitchBlock : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::getFirstNodeInsideSwitchBlock to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return firstNodeInsideBlock; |
715 | } | |
716 | ||
717 | /** | |
718 | * Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is | |
719 | * met only inside the block (not in its declaration!). | |
720 | * @param block | |
721 | * Ast node represents TRY-CATCH-FINALLY block. | |
722 | * @param variable | |
723 | * Variable which is checked for content in block. | |
724 | * @return If variable usage is met only inside the block | |
725 | * (not in its declaration!) than return the first Ast node | |
726 | * of this block, otherwise - null. | |
727 | */ | |
728 | private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks( | |
729 | DetailAST block, DetailAST variable) { | |
730 | DetailAST currentNode = block.getFirstChild(); | |
731 | final List<DetailAST> variableUsageExpressions = | |
732 | new ArrayList<>(); | |
733 | ||
734 | // Checking variable usage inside TRY block. | |
735 |
1
1. getFirstNodeInsideTryCatchFinallyBlocks : negated conditional → KILLED |
if (isChild(currentNode, variable)) { |
736 | variableUsageExpressions.add(currentNode); | |
737 | } | |
738 | ||
739 | // Switch on CATCH block. | |
740 | currentNode = currentNode.getNextSibling(); | |
741 | ||
742 | // Checking variable usage inside all CATCH blocks. | |
743 |
1
1. getFirstNodeInsideTryCatchFinallyBlocks : negated conditional → SURVIVED |
while (currentNode != null |
744 |
1
1. getFirstNodeInsideTryCatchFinallyBlocks : negated conditional → SURVIVED |
&& currentNode.getType() == TokenTypes.LITERAL_CATCH) { |
745 | final DetailAST catchBlock = currentNode.getLastChild(); | |
746 | ||
747 |
1
1. getFirstNodeInsideTryCatchFinallyBlocks : negated conditional → KILLED |
if (isChild(catchBlock, variable)) { |
748 | variableUsageExpressions.add(catchBlock); | |
749 | } | |
750 | currentNode = currentNode.getNextSibling(); | |
751 | } | |
752 | ||
753 | // Checking variable usage inside FINALLY block. | |
754 |
1
1. getFirstNodeInsideTryCatchFinallyBlocks : negated conditional → KILLED |
if (currentNode != null) { |
755 | final DetailAST finalBlock = currentNode.getLastChild(); | |
756 | ||
757 |
1
1. getFirstNodeInsideTryCatchFinallyBlocks : negated conditional → KILLED |
if (isChild(finalBlock, variable)) { |
758 | variableUsageExpressions.add(finalBlock); | |
759 | } | |
760 | } | |
761 | ||
762 | DetailAST variableUsageNode = null; | |
763 | ||
764 | // If variable usage exists in several related blocks, then | |
765 | // firstNodeInsideBlock = null, otherwise if variable usage exists | |
766 | // only inside one block, then get node from | |
767 | // variableUsageExpressions. | |
768 |
1
1. getFirstNodeInsideTryCatchFinallyBlocks : negated conditional → KILLED |
if (variableUsageExpressions.size() == 1) { |
769 | variableUsageNode = variableUsageExpressions.get(0).getFirstChild(); | |
770 | } | |
771 | ||
772 |
1
1. getFirstNodeInsideTryCatchFinallyBlocks : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/VariableDeclarationUsageDistanceCheck::getFirstNodeInsideTryCatchFinallyBlocks to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return variableUsageNode; |
773 | } | |
774 | ||
775 | /** | |
776 | * Checks if variable is in operator declaration. For instance: | |
777 | * <pre> | |
778 | * boolean b = true; | |
779 | * if (b) {...} | |
780 | * </pre> | |
781 | * Variable 'b' is in declaration of operator IF. | |
782 | * @param operator | |
783 | * Ast node which represents operator. | |
784 | * @param variable | |
785 | * Variable which is checked for content in operator. | |
786 | * @return true if operator contains variable in its declaration, otherwise | |
787 | * - false. | |
788 | */ | |
789 | private static boolean isVariableInOperatorExpr( | |
790 | DetailAST operator, DetailAST variable) { | |
791 | boolean isVarInOperatorDeclaration = false; | |
792 | final DetailAST openingBracket = | |
793 | operator.findFirstToken(TokenTypes.LPAREN); | |
794 | ||
795 | // Get EXPR between brackets | |
796 | DetailAST exprBetweenBrackets = openingBracket.getNextSibling(); | |
797 | ||
798 | // Look if variable is in operator expression | |
799 |
1
1. isVariableInOperatorExpr : negated conditional → KILLED |
while (exprBetweenBrackets.getType() != TokenTypes.RPAREN) { |
800 |
1
1. isVariableInOperatorExpr : negated conditional → KILLED |
if (isChild(exprBetweenBrackets, variable)) { |
801 | isVarInOperatorDeclaration = true; | |
802 | break; | |
803 | } | |
804 | exprBetweenBrackets = exprBetweenBrackets.getNextSibling(); | |
805 | } | |
806 | ||
807 | // Variable may be met in ELSE declaration | |
808 | // So, check variable usage in these declarations. | |
809 |
2
1. isVariableInOperatorExpr : negated conditional → SURVIVED 2. isVariableInOperatorExpr : negated conditional → SURVIVED |
if (!isVarInOperatorDeclaration && operator.getType() == TokenTypes.LITERAL_IF) { |
810 | final DetailAST elseBlock = operator.getLastChild(); | |
811 | ||
812 |
1
1. isVariableInOperatorExpr : negated conditional → SURVIVED |
if (elseBlock.getType() == TokenTypes.LITERAL_ELSE) { |
813 | // Get IF followed by ELSE | |
814 | final DetailAST firstNodeInsideElseBlock = elseBlock.getFirstChild(); | |
815 | ||
816 |
1
1. isVariableInOperatorExpr : negated conditional → KILLED |
if (firstNodeInsideElseBlock.getType() == TokenTypes.LITERAL_IF) { |
817 | isVarInOperatorDeclaration = | |
818 | isVariableInOperatorExpr(firstNodeInsideElseBlock, variable); | |
819 | } | |
820 | } | |
821 | } | |
822 | ||
823 |
1
1. isVariableInOperatorExpr : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return isVarInOperatorDeclaration; |
824 | } | |
825 | ||
826 | /** | |
827 | * Checks if Ast node contains given element. | |
828 | * @param parent | |
829 | * Node of AST. | |
830 | * @param ast | |
831 | * Ast element which is checked for content in Ast node. | |
832 | * @return true if Ast element was found in Ast node, otherwise - false. | |
833 | */ | |
834 | private static boolean isChild(DetailAST parent, DetailAST ast) { | |
835 | boolean isChild = false; | |
836 | final ASTEnumeration astList = parent.findAllPartial(ast); | |
837 | ||
838 |
1
1. isChild : negated conditional → KILLED |
while (astList.hasMoreNodes()) { |
839 | final DetailAST astNode = (DetailAST) astList.nextNode(); | |
840 | DetailAST astParent = astNode.getParent(); | |
841 | ||
842 |
1
1. isChild : negated conditional → KILLED |
while (astParent != null) { |
843 |
1
1. isChild : negated conditional → KILLED |
if (astParent.equals(parent) |
844 |
1
1. isChild : negated conditional → KILLED |
&& astParent.getLineNo() == parent.getLineNo()) { |
845 | isChild = true; | |
846 | break; | |
847 | } | |
848 | astParent = astParent.getParent(); | |
849 | } | |
850 | } | |
851 | ||
852 |
1
1. isChild : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return isChild; |
853 | } | |
854 | ||
855 | /** | |
856 | * Checks if entrance variable is contained in ignored pattern. | |
857 | * @param variable | |
858 | * Variable which is checked for content in ignored pattern. | |
859 | * @return true if variable was found, otherwise - false. | |
860 | */ | |
861 | private boolean isVariableMatchesIgnorePattern(String variable) { | |
862 | final Matcher matcher = ignoreVariablePattern.matcher(variable); | |
863 |
1
1. isVariableMatchesIgnorePattern : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return matcher.matches(); |
864 | } | |
865 | ||
866 | } | |
Mutations | ||
245 |
1.1 |
|
250 |
1.1 |
|
255 |
1.1 |
|
263 |
1.1 2.2 |
|
264 |
1.1 |
|
267 |
1.1 |
|
270 |
1.1 |
|
278 |
1.1 2.2 |
|
279 |
1.1 |
|
280 |
1.1 |
|
281 |
1.1 |
|
285 |
1.1 |
|
304 |
1.1 |
|
307 |
1.1 |
|
327 |
1.1 2.2 3.3 |
|
334 |
1.1 |
|
338 |
1.1 |
|
342 |
1.1 |
|
343 |
1.1 |
|
373 |
1.1 |
|
393 |
1.1 2.2 |
|
394 |
1.1 |
|
395 |
1.1 |
|
396 |
1.1 |
|
401 |
1.1 |
|
402 |
1.1 |
|
409 |
1.1 |
|
413 |
1.1 |
|
426 |
1.1 |
|
433 |
1.1 |
|
443 |
1.1 |
|
444 |
1.1 |
|
453 |
1.1 |
|
454 |
1.1 |
|
460 |
1.1 |
|
478 |
1.1 |
|
485 |
1.1 |
|
489 |
1.1 |
|
496 |
1.1 |
|
521 |
1.1 |
|
530 |
1.1 |
|
536 |
1.1 |
|
540 |
1.1 |
|
555 |
1.1 |
|
556 |
1.1 |
|
557 |
1.1 |
|
558 |
1.1 |
|
563 |
1.1 |
|
564 |
1.1 |
|
565 |
1.1 |
|
570 |
1.1 |
|
588 |
1.1 |
|
592 |
1.1 |
|
604 |
1.1 |
|
607 |
1.1 |
|
612 |
1.1 |
|
630 |
1.1 |
|
635 |
1.1 |
|
636 |
1.1 |
|
641 |
1.1 |
|
648 |
1.1 |
|
651 |
1.1 |
|
659 |
1.1 |
|
660 |
1.1 |
|
668 |
1.1 |
|
673 |
1.1 |
|
695 |
1.1 |
|
699 |
1.1 |
|
710 |
1.1 |
|
714 |
1.1 |
|
735 |
1.1 |
|
743 |
1.1 |
|
744 |
1.1 |
|
747 |
1.1 |
|
754 |
1.1 |
|
757 |
1.1 |
|
768 |
1.1 |
|
772 |
1.1 |
|
799 |
1.1 |
|
800 |
1.1 |
|
809 |
1.1 2.2 |
|
812 |
1.1 |
|
816 |
1.1 |
|
823 |
1.1 |
|
838 |
1.1 |
|
842 |
1.1 |
|
843 |
1.1 |
|
844 |
1.1 |
|
852 |
1.1 |
|
863 |
1.1 |