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