View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 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.Optional;
27  import java.util.Set;
28  import java.util.regex.Matcher;
29  import java.util.regex.Pattern;
30  
31  import com.puppycrawl.tools.checkstyle.StatelessCheck;
32  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
33  import com.puppycrawl.tools.checkstyle.api.DetailAST;
34  import com.puppycrawl.tools.checkstyle.api.FullIdent;
35  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
36  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
37  
38  /**
39   * <div>
40   * Checks the distance between declaration of variable and its first usage.
41   * Note: Any additional variables declared or initialized between the declaration and
42   *  the first usage of the said variable are not counted when calculating the distance.
43   * </div>
44   *
45   * @since 5.8
46   */
47  @StatelessCheck
48  public class VariableDeclarationUsageDistanceCheck extends AbstractCheck {
49  
50      /**
51       * Warning message key.
52       */
53      public static final String MSG_KEY = "variable.declaration.usage.distance";
54  
55      /**
56       * Warning message key.
57       */
58      public static final String MSG_KEY_EXT = "variable.declaration.usage.distance.extend";
59  
60      /**
61       * Default value of distance between declaration of variable and its first
62       * usage.
63       */
64      private static final int DEFAULT_DISTANCE = 3;
65  
66      /** Tokens that should be ignored when calculating usage distance. */
67      private static final Set<Integer> ZERO_DISTANCE_TOKENS = Set.of(
68              TokenTypes.VARIABLE_DEF,
69              TokenTypes.TYPE,
70              TokenTypes.MODIFIERS,
71              TokenTypes.RESOURCE,
72              TokenTypes.EXTENDS_CLAUSE,
73              TokenTypes.IMPLEMENTS_CLAUSE,
74              TokenTypes.TYPE_PARAMETERS,
75              TokenTypes.PARAMETERS,
76              TokenTypes.LITERAL_THROWS
77      );
78  
79      /**
80       * Specify the maximum distance between a variable's declaration and its first usage.
81       * Value should be greater than 0.
82       */
83      private int allowedDistance = DEFAULT_DISTANCE;
84  
85      /**
86       * Define RegExp to ignore distance calculation for variables listed in
87       * this pattern.
88       */
89      private Pattern ignoreVariablePattern = Pattern.compile("");
90  
91      /**
92       * Allow to calculate the distance between a variable's declaration and its first usage
93       * across different scopes.
94       */
95      private boolean validateBetweenScopes;
96  
97      /** Allow to ignore variables with a 'final' modifier. */
98      private boolean ignoreFinal = true;
99  
100     /**
101      * Setter to specify the maximum distance between a variable's declaration and its first usage.
102      * Value should be greater than 0.
103      *
104      * @param allowedDistance
105      *        Allowed distance between declaration of variable and its first
106      *        usage.
107      * @since 5.8
108      */
109     public void setAllowedDistance(int allowedDistance) {
110         this.allowedDistance = allowedDistance;
111     }
112 
113     /**
114      * Setter to define RegExp to ignore distance calculation for variables listed in this pattern.
115      *
116      * @param pattern a pattern.
117      * @since 5.8
118      */
119     public void setIgnoreVariablePattern(Pattern pattern) {
120         ignoreVariablePattern = pattern;
121     }
122 
123     /**
124      * Setter to allow to calculate the distance between a variable's declaration
125      * and its first usage across different scopes.
126      *
127      * @param validateBetweenScopes
128      *        Defines if allow to calculate distance between declaration of
129      *        variable and its first usage in different scopes or not.
130      * @since 5.8
131      */
132     public void setValidateBetweenScopes(boolean validateBetweenScopes) {
133         this.validateBetweenScopes = validateBetweenScopes;
134     }
135 
136     /**
137      * Setter to allow to ignore variables with a 'final' modifier.
138      *
139      * @param ignoreFinal
140      *        Defines if ignore variables with 'final' modifier or not.
141      * @since 5.8
142      */
143     public void setIgnoreFinal(boolean ignoreFinal) {
144         this.ignoreFinal = ignoreFinal;
145     }
146 
147     @Override
148     public int[] getDefaultTokens() {
149         return getRequiredTokens();
150     }
151 
152     @Override
153     public int[] getAcceptableTokens() {
154         return getRequiredTokens();
155     }
156 
157     @Override
158     public int[] getRequiredTokens() {
159         return new int[] {TokenTypes.VARIABLE_DEF};
160     }
161 
162     @Override
163     public void visitToken(DetailAST ast) {
164         final int parentType = ast.getParent().getType();
165         final DetailAST modifiers = ast.getFirstChild();
166 
167         if (parentType != TokenTypes.OBJBLOCK
168                 && (!ignoreFinal || modifiers.findFirstToken(TokenTypes.FINAL) == null)) {
169             final DetailAST variable = ast.findFirstToken(TokenTypes.IDENT);
170 
171             if (!isVariableMatchesIgnorePattern(variable.getText())) {
172                 final DetailAST semicolonAst = ast.getNextSibling();
173                 final Entry<DetailAST, Integer> entry;
174                 if (validateBetweenScopes) {
175                     entry = calculateDistanceBetweenScopes(semicolonAst, variable);
176                 }
177                 else {
178                     entry = calculateDistanceInSingleScope(semicolonAst, variable);
179                 }
180                 final DetailAST variableUsageAst = entry.getKey();
181                 final int dist = entry.getValue();
182                 if (dist > allowedDistance
183                         && !isInitializationSequence(variableUsageAst, variable.getText())) {
184                     if (ignoreFinal) {
185                         log(ast, MSG_KEY_EXT, variable.getText(), dist, allowedDistance);
186                     }
187                     else {
188                         log(ast, MSG_KEY, variable.getText(), dist, allowedDistance);
189                     }
190                 }
191             }
192         }
193     }
194 
195     /**
196      * Get name of instance whose method is called.
197      *
198      * @param methodCallAst
199      *        DetailAST of METHOD_CALL.
200      * @return name of instance.
201      */
202     private static String getInstanceName(DetailAST methodCallAst) {
203         final String methodCallName =
204                 FullIdent.createFullIdentBelow(methodCallAst).getText();
205         final int lastDotIndex = methodCallName.lastIndexOf('.');
206         String instanceName = "";
207         if (lastDotIndex != -1) {
208             instanceName = methodCallName.substring(0, lastDotIndex);
209         }
210         return instanceName;
211     }
212 
213     /**
214      * Processes statements until usage of variable to detect sequence of
215      * initialization methods.
216      *
217      * @param variableUsageAst
218      *        DetailAST of expression that uses variable named variableName.
219      * @param variableName
220      *        name of considered variable.
221      * @return true if statements between declaration and usage of variable are
222      *         initialization methods.
223      */
224     private static boolean isInitializationSequence(
225             DetailAST variableUsageAst, String variableName) {
226         boolean result = true;
227         boolean isUsedVariableDeclarationFound = false;
228         DetailAST currentSiblingAst = variableUsageAst;
229         String initInstanceName = "";
230 
231         while (result && !isUsedVariableDeclarationFound && currentSiblingAst != null) {
232             if (currentSiblingAst.getType() == TokenTypes.EXPR
233                     && currentSiblingAst.getFirstChild().getType() == TokenTypes.METHOD_CALL) {
234                 final DetailAST methodCallAst = currentSiblingAst.getFirstChild();
235                 final String instanceName = getInstanceName(methodCallAst);
236                 if (instanceName.isEmpty()) {
237                     result = false;
238                 }
239                 else if (!instanceName.equals(initInstanceName)) {
240                     if (initInstanceName.isEmpty()) {
241                         initInstanceName = instanceName;
242                     }
243                     else {
244                         result = false;
245                     }
246                 }
247 
248             }
249             else if (currentSiblingAst.getType() == TokenTypes.VARIABLE_DEF) {
250                 final String currentVariableName =
251                         currentSiblingAst.findFirstToken(TokenTypes.IDENT).getText();
252                 isUsedVariableDeclarationFound = variableName.equals(currentVariableName);
253             }
254             else {
255                 result = currentSiblingAst.getType() == TokenTypes.SEMI;
256             }
257             currentSiblingAst = currentSiblingAst.getPreviousSibling();
258         }
259         return result;
260     }
261 
262     /**
263      * Calculates distance between declaration of variable and its first usage
264      * in single scope.
265      *
266      * @param semicolonAst
267      *        Regular node of Ast which is checked for content of checking
268      *        variable.
269      * @param variableIdentAst
270      *        Variable which distance is calculated for.
271      * @return entry which contains expression with variable usage and distance.
272      *         If variable usage is not found, then the expression node is null,
273      *         although the distance can be greater than zero.
274      */
275     private static Entry<DetailAST, Integer> calculateDistanceInSingleScope(
276             DetailAST semicolonAst, DetailAST variableIdentAst) {
277         int dist = 0;
278         boolean firstUsageFound = false;
279         DetailAST currentAst = semicolonAst;
280         DetailAST variableUsageAst = null;
281 
282         while (!firstUsageFound && currentAst != null) {
283             if (currentAst.getFirstChild() != null) {
284                 if (isChild(currentAst, variableIdentAst)) {
285                     dist = getDistToVariableUsageInChildNode(currentAst, dist);
286                     variableUsageAst = currentAst;
287                     firstUsageFound = true;
288                 }
289                 else if (currentAst.getType() != TokenTypes.VARIABLE_DEF) {
290                     dist++;
291                 }
292             }
293             currentAst = currentAst.getNextSibling();
294         }
295 
296         return new SimpleEntry<>(variableUsageAst, dist);
297     }
298 
299     /**
300      * Returns the distance to variable usage for in the child node.
301      *
302      * @param childNode child node.
303      * @param currentDistToVarUsage current distance to the variable usage.
304      * @return the distance to variable usage for in the child node.
305      */
306     private static int getDistToVariableUsageInChildNode(DetailAST childNode,
307                                                          int currentDistToVarUsage) {
308         return switch (childNode.getType()) {
309             case TokenTypes.SLIST -> 0;
310             case TokenTypes.LITERAL_FOR,
311                  TokenTypes.LITERAL_WHILE,
312                  TokenTypes.LITERAL_DO,
313                  TokenTypes.LITERAL_IF,
314                  TokenTypes.LITERAL_TRY -> currentDistToVarUsage + 1;
315             default -> {
316                 if (childNode.findFirstToken(TokenTypes.SLIST) == null) {
317                     yield currentDistToVarUsage + 1;
318                 }
319                 yield 0;
320             }
321         };
322     }
323 
324     /**
325      * Calculates distance between declaration of variable and its first usage
326      * in multiple scopes.
327      *
328      * @param ast
329      *        Regular node of Ast which is checked for content of checking
330      *        variable.
331      * @param variable
332      *        Variable which distance is calculated for.
333      * @return entry which contains expression with variable usage and distance.
334      */
335     private static Entry<DetailAST, Integer> calculateDistanceBetweenScopes(
336             DetailAST ast, DetailAST variable) {
337         int dist = 0;
338         DetailAST currentScopeAst = ast;
339         DetailAST variableUsageAst = null;
340         while (currentScopeAst != null) {
341             final Entry<List<DetailAST>, Integer> searchResult =
342                     searchVariableUsageExpressions(variable, currentScopeAst);
343 
344             currentScopeAst = null;
345 
346             final List<DetailAST> variableUsageExpressions = searchResult.getKey();
347             dist += searchResult.getValue();
348 
349             // If variable usage exists in a single scope, then look into
350             // this scope and count distance until variable usage.
351             if (variableUsageExpressions.size() == 1) {
352                 final DetailAST blockWithVariableUsage = variableUsageExpressions.getFirst();
353                 currentScopeAst = switch (blockWithVariableUsage.getType()) {
354                     case TokenTypes.VARIABLE_DEF, TokenTypes.EXPR -> {
355                         dist++;
356                         yield null;
357                     }
358                     case TokenTypes.LITERAL_FOR, TokenTypes.LITERAL_WHILE, TokenTypes.LITERAL_DO ->
359                         getFirstNodeInsideForWhileDoWhileBlocks(blockWithVariableUsage, variable);
360                     case TokenTypes.LITERAL_IF ->
361                         getFirstNodeInsideIfBlock(blockWithVariableUsage, variable);
362                     case TokenTypes.LITERAL_SWITCH ->
363                         getFirstNodeInsideSwitchBlock(blockWithVariableUsage, variable);
364                     case TokenTypes.LITERAL_TRY ->
365                         getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage, variable);
366                     default -> blockWithVariableUsage.getFirstChild();
367                 };
368                 variableUsageAst = blockWithVariableUsage;
369             }
370 
371             // If there's no any variable usage, then distance = 0.
372             else if (variableUsageExpressions.isEmpty()) {
373                 variableUsageAst = null;
374             }
375             // If variable usage exists in different scopes, then distance =
376             // distance until variable first usage.
377             else {
378                 dist++;
379                 variableUsageAst = variableUsageExpressions.getFirst();
380             }
381         }
382         return new SimpleEntry<>(variableUsageAst, dist);
383     }
384 
385     /**
386      * Searches variable usages starting from specified statement.
387      *
388      * @param variableAst Variable that is used.
389      * @param statementAst DetailAST to start searching from.
390      * @return entry which contains list with found expressions that use the variable
391      *     and distance from specified statement to first found expression.
392      */
393     private static Entry<List<DetailAST>, Integer>
394         searchVariableUsageExpressions(final DetailAST variableAst, final DetailAST statementAst) {
395         final List<DetailAST> variableUsageExpressions = new ArrayList<>();
396         int distance = 0;
397         DetailAST currentStatementAst = statementAst;
398         while (currentStatementAst != null) {
399             if (currentStatementAst.getFirstChild() != null) {
400                 if (isChild(currentStatementAst, variableAst)) {
401                     variableUsageExpressions.add(currentStatementAst);
402                 }
403                 // If expression hasn't been met yet, then distance + 1.
404                 else if (variableUsageExpressions.isEmpty()
405                         && !isZeroDistanceToken(currentStatementAst.getType())) {
406                     distance++;
407                 }
408             }
409             currentStatementAst = currentStatementAst.getNextSibling();
410         }
411         return new SimpleEntry<>(variableUsageExpressions, distance);
412     }
413 
414     /**
415      * Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable
416      * usage is met only inside the block (not in its declaration!).
417      *
418      * @param block
419      *        Ast node represents FOR, WHILE or DO-WHILE block.
420      * @param variable
421      *        Variable which is checked for content in block.
422      * @return If variable usage is met only inside the block
423      *         (not in its declaration!) then return the first Ast node
424      *         of this block, otherwise - null.
425      */
426     private static DetailAST getFirstNodeInsideForWhileDoWhileBlocks(
427             DetailAST block, DetailAST variable) {
428         DetailAST firstNodeInsideBlock = null;
429 
430         if (!isVariableInOperatorExpr(block, variable)) {
431             final DetailAST currentNode;
432 
433             // Find currentNode for DO-WHILE block.
434             if (block.getType() == TokenTypes.LITERAL_DO) {
435                 currentNode = block.getFirstChild();
436             }
437             // Find currentNode for FOR or WHILE block.
438             else {
439                 // Looking for RPAREN ( ')' ) token to mark the end of operator
440                 // expression.
441                 currentNode = block.findFirstToken(TokenTypes.RPAREN).getNextSibling();
442             }
443 
444             final int currentNodeType = currentNode.getType();
445 
446             if (currentNodeType != TokenTypes.EXPR) {
447                 firstNodeInsideBlock = currentNode;
448             }
449         }
450 
451         return firstNodeInsideBlock;
452     }
453 
454     /**
455      * Gets first Ast node inside IF block if variable usage is met
456      * only inside the block (not in its declaration!).
457      *
458      * @param block
459      *        Ast node represents IF block.
460      * @param variable
461      *        Variable which is checked for content in block.
462      * @return If variable usage is met only inside the block
463      *         (not in its declaration!) then return the first Ast node
464      *         of this block, otherwise - null.
465      */
466     private static DetailAST getFirstNodeInsideIfBlock(
467             DetailAST block, DetailAST variable) {
468         DetailAST firstNodeInsideBlock = null;
469 
470         if (!isVariableInOperatorExpr(block, variable)) {
471             final Optional<DetailAST> slistToken = TokenUtil
472                 .findFirstTokenByPredicate(block, token -> token.getType() == TokenTypes.SLIST);
473             final DetailAST lastNode = block.getLastChild();
474             DetailAST previousNode = lastNode.getPreviousSibling();
475 
476             if (slistToken.isEmpty()
477                 && lastNode.getType() == TokenTypes.LITERAL_ELSE) {
478 
479                 // Is if statement without '{}' and has a following else branch,
480                 // then change previousNode to the if statement body.
481                 previousNode = previousNode.getPreviousSibling();
482             }
483 
484             final List<DetailAST> variableUsageExpressions = new ArrayList<>();
485             if (isChild(previousNode, variable)) {
486                 variableUsageExpressions.add(previousNode);
487             }
488 
489             if (isChild(lastNode, variable)) {
490                 variableUsageExpressions.add(lastNode);
491             }
492 
493             // If variable usage exists in several related blocks, then
494             // firstNodeInsideBlock = null, otherwise if variable usage exists
495             // only inside one block, then get node from
496             // variableUsageExpressions.
497             if (variableUsageExpressions.size() == 1) {
498                 firstNodeInsideBlock = variableUsageExpressions.getFirst();
499             }
500         }
501 
502         return firstNodeInsideBlock;
503     }
504 
505     /**
506      * Gets first Ast node inside SWITCH block if variable usage is met
507      * only inside the block (not in its declaration!).
508      *
509      * @param block
510      *        Ast node represents SWITCH block.
511      * @param variable
512      *        Variable which is checked for content in block.
513      * @return If variable usage is met only inside the block
514      *         (not in its declaration!) then return the first Ast node
515      *         of this block, otherwise - null.
516      */
517     private static DetailAST getFirstNodeInsideSwitchBlock(
518             DetailAST block, DetailAST variable) {
519         final List<DetailAST> variableUsageExpressions =
520                 getVariableUsageExpressionsInsideSwitchBlock(block, variable);
521 
522         // If variable usage exists in several related blocks, then
523         // firstNodeInsideBlock = null, otherwise if variable usage exists
524         // only inside one block, then get node from
525         // variableUsageExpressions.
526         DetailAST firstNodeInsideBlock = null;
527         if (variableUsageExpressions.size() == 1) {
528             firstNodeInsideBlock = variableUsageExpressions.getFirst();
529         }
530 
531         return firstNodeInsideBlock;
532     }
533 
534     /**
535      * Helper method for getFirstNodeInsideSwitchBlock to return all variable
536      * usage expressions inside a given switch block.
537      *
538      * @param block the switch block to check.
539      * @param variable variable which is checked for in switch block.
540      * @return List of usages or empty list if none are found.
541      */
542     private static List<DetailAST> getVariableUsageExpressionsInsideSwitchBlock(DetailAST block,
543                                                                             DetailAST variable) {
544         final Optional<DetailAST> firstToken = TokenUtil.findFirstTokenByPredicate(block, child -> {
545             return child.getType() == TokenTypes.SWITCH_RULE
546                     || child.getType() == TokenTypes.CASE_GROUP;
547         });
548 
549         final List<DetailAST> variableUsageExpressions = new ArrayList<>();
550 
551         firstToken.ifPresent(token -> {
552             TokenUtil.forEachChild(block, token.getType(), child -> {
553                 final DetailAST lastNodeInCaseGroup = child.getLastChild();
554                 if (isChild(lastNodeInCaseGroup, variable)) {
555                     variableUsageExpressions.add(lastNodeInCaseGroup);
556                 }
557             });
558         });
559 
560         return variableUsageExpressions;
561     }
562 
563     /**
564      * Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is
565      * met only inside the block (not in its declaration!).
566      *
567      * @param block
568      *        Ast node represents TRY-CATCH-FINALLY block.
569      * @param variable
570      *        Variable which is checked for content in block.
571      * @return If variable usage is met only inside the block
572      *         (not in its declaration!) then return the first Ast node
573      *         of this block, otherwise - null.
574      */
575     private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks(
576             DetailAST block, DetailAST variable) {
577         DetailAST currentNode = block.getFirstChild();
578         final List<DetailAST> variableUsageExpressions =
579                 new ArrayList<>();
580 
581         // Checking variable usage inside TRY block.
582         if (isChild(currentNode, variable)) {
583             variableUsageExpressions.add(currentNode);
584         }
585 
586         // Switch on CATCH block.
587         currentNode = currentNode.getNextSibling();
588 
589         // Checking variable usage inside all CATCH blocks.
590         while (currentNode != null
591                 && currentNode.getType() == TokenTypes.LITERAL_CATCH) {
592             final DetailAST catchBlock = currentNode.getLastChild();
593 
594             if (isChild(catchBlock, variable)) {
595                 variableUsageExpressions.add(catchBlock);
596             }
597             currentNode = currentNode.getNextSibling();
598         }
599 
600         // Checking variable usage inside FINALLY block.
601         if (currentNode != null) {
602             final DetailAST finalBlock = currentNode.getLastChild();
603 
604             if (isChild(finalBlock, variable)) {
605                 variableUsageExpressions.add(finalBlock);
606             }
607         }
608 
609         DetailAST variableUsageNode = null;
610 
611         // If variable usage exists in several related blocks, then
612         // firstNodeInsideBlock = null, otherwise if variable usage exists
613         // only inside one block, then get node from
614         // variableUsageExpressions.
615         if (variableUsageExpressions.size() == 1) {
616             variableUsageNode = variableUsageExpressions.getFirst().getFirstChild();
617         }
618 
619         return variableUsageNode;
620     }
621 
622     /**
623      * Checks if variable is in operator declaration. For instance:
624      * <pre>
625      * boolean b = true;
626      * if (b) {...}
627      * </pre>
628      * Variable 'b' is in declaration of operator IF.
629      *
630      * @param operator
631      *        Ast node which represents operator.
632      * @param variable
633      *        Variable which is checked for content in operator.
634      * @return true if operator contains variable in its declaration, otherwise
635      *         - false.
636      */
637     private static boolean isVariableInOperatorExpr(
638             DetailAST operator, DetailAST variable) {
639         boolean isVarInOperatorDeclaration = false;
640 
641         DetailAST ast = operator.findFirstToken(TokenTypes.LPAREN);
642 
643         // Look if variable is in operator expression
644         while (ast.getType() != TokenTypes.RPAREN) {
645             if (isChild(ast, variable)) {
646                 isVarInOperatorDeclaration = true;
647                 break;
648             }
649             ast = ast.getNextSibling();
650         }
651 
652         return isVarInOperatorDeclaration;
653     }
654 
655     /**
656      * Checks if Ast node contains given element.
657      *
658      * @param parent
659      *        Node of AST.
660      * @param ast
661      *        Ast element which is checked for content in Ast node.
662      * @return true if Ast element was found in Ast node, otherwise - false.
663      */
664     private static boolean isChild(DetailAST parent, DetailAST ast) {
665         boolean isChild = false;
666         DetailAST curNode = parent.getFirstChild();
667 
668         while (curNode != null) {
669             if (curNode.getType() == ast.getType() && curNode.getText().equals(ast.getText())) {
670                 isChild = true;
671                 break;
672             }
673 
674             DetailAST toVisit = curNode.getFirstChild();
675             while (toVisit == null) {
676                 toVisit = curNode.getNextSibling();
677                 curNode = curNode.getParent();
678 
679                 if (curNode == parent) {
680                     break;
681                 }
682             }
683 
684             curNode = toVisit;
685         }
686 
687         return isChild;
688     }
689 
690     /**
691      * Checks if entrance variable is contained in ignored pattern.
692      *
693      * @param variable
694      *        Variable which is checked for content in ignored pattern.
695      * @return true if variable was found, otherwise - false.
696      */
697     private boolean isVariableMatchesIgnorePattern(String variable) {
698         final Matcher matcher = ignoreVariablePattern.matcher(variable);
699         return matcher.matches();
700     }
701 
702     /**
703      * Check if the token should be ignored for distance counting.
704      * For example,
705      * <pre>
706      *     try (final AutoCloseable t = new java.io.StringReader(a);) {
707      *     }
708      * </pre>
709      * final is a zero-distance token and should be ignored for distance counting.
710      * <pre>
711      *     class Table implements Comparator&lt;Integer&gt;{
712      *     }
713      * </pre>
714      * An inner class may be defined. Both tokens implements and extends
715      * are zero-distance tokens.
716      * <pre>
717      *     public int method(Object b){
718      *     }
719      * </pre>
720      * public is a modifier and zero-distance token. int is a type and
721      * zero-distance token.
722      *
723      * @param type
724      *        Token type of the ast node.
725      * @return true if it should be ignored for distance counting, otherwise false.
726      */
727     private static boolean isZeroDistanceToken(int type) {
728         return ZERO_DISTANCE_TOKENS.contains(type);
729     }
730 
731 }