Coverage Report - com.puppycrawl.tools.checkstyle.checks.coding.ModifiedControlVariableCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
ModifiedControlVariableCheck
95%
57/60
40%
25/62
5.727
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2014  Oliver Burn
 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  
 package com.puppycrawl.tools.checkstyle.checks.coding;
 20  
 
 21  
 import com.puppycrawl.tools.checkstyle.api.Check;
 22  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 23  
 import com.puppycrawl.tools.checkstyle.api.FastStack;
 24  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 25  
 /**
 26  
  * Check for ensuring that for loop control variables are not modified
 27  
  * inside the for block.
 28  
  *
 29  
  * @author Daniel Grenner
 30  
  */
 31  1
 public final class ModifiedControlVariableCheck extends Check
 32  
 {
 33  
     /** Current set of parameters. */
 34  1
     private FastStack<String> mCurrentVariables = FastStack.newInstance();
 35  
     /** Stack of block parameters. */
 36  1
     private final FastStack<FastStack<String>> mVariableStack =
 37  
         FastStack.newInstance();
 38  
 
 39  
     @Override
 40  
     public int[] getDefaultTokens()
 41  
     {
 42  1
         return new int[] {
 43  
             TokenTypes.OBJBLOCK,
 44  
             TokenTypes.LITERAL_FOR,
 45  
             TokenTypes.FOR_ITERATOR,
 46  
             TokenTypes.FOR_EACH_CLAUSE,
 47  
             TokenTypes.ASSIGN,
 48  
             TokenTypes.PLUS_ASSIGN,
 49  
             TokenTypes.MINUS_ASSIGN,
 50  
             TokenTypes.STAR_ASSIGN,
 51  
             TokenTypes.DIV_ASSIGN,
 52  
             TokenTypes.MOD_ASSIGN,
 53  
             TokenTypes.SR_ASSIGN,
 54  
             TokenTypes.BSR_ASSIGN,
 55  
             TokenTypes.SL_ASSIGN,
 56  
             TokenTypes.BAND_ASSIGN,
 57  
             TokenTypes.BXOR_ASSIGN,
 58  
             TokenTypes.BOR_ASSIGN,
 59  
             TokenTypes.INC,
 60  
             TokenTypes.POST_INC,
 61  
             TokenTypes.DEC,
 62  
             TokenTypes.POST_DEC,
 63  
         };
 64  
     }
 65  
 
 66  
     @Override
 67  
     public int[] getRequiredTokens()
 68  
     {
 69  0
         return getDefaultTokens();
 70  
     }
 71  
 
 72  
     @Override
 73  
     public void beginTree(DetailAST aRootAST)
 74  
     {
 75  
         // clear data
 76  1
         mCurrentVariables.clear();
 77  1
         mVariableStack.clear();
 78  1
     }
 79  
 
 80  
     @Override
 81  
     public void visitToken(DetailAST aAST)
 82  
     {
 83  54
         switch (aAST.getType()) {
 84  
         case TokenTypes.OBJBLOCK:
 85  2
             enterBlock();
 86  2
             break;
 87  
         case TokenTypes.LITERAL_FOR:
 88  
         case TokenTypes.FOR_ITERATOR:
 89  
         case TokenTypes.FOR_EACH_CLAUSE:
 90  22
             break;
 91  
         case TokenTypes.ASSIGN:
 92  
         case TokenTypes.PLUS_ASSIGN:
 93  
         case TokenTypes.MINUS_ASSIGN:
 94  
         case TokenTypes.STAR_ASSIGN:
 95  
         case TokenTypes.DIV_ASSIGN:
 96  
         case TokenTypes.MOD_ASSIGN:
 97  
         case TokenTypes.SR_ASSIGN:
 98  
         case TokenTypes.BSR_ASSIGN:
 99  
         case TokenTypes.SL_ASSIGN:
 100  
         case TokenTypes.BAND_ASSIGN:
 101  
         case TokenTypes.BXOR_ASSIGN:
 102  
         case TokenTypes.BOR_ASSIGN:
 103  
         case TokenTypes.INC:
 104  
         case TokenTypes.POST_INC:
 105  
         case TokenTypes.DEC:
 106  
         case TokenTypes.POST_DEC:
 107  30
             checkIdent(aAST);
 108  30
             break;
 109  
         default:
 110  0
             throw new IllegalStateException(aAST.toString());
 111  
         }
 112  54
     }
 113  
 
 114  
 
 115  
     @Override
 116  
     public void leaveToken(DetailAST aAST)
 117  
     {
 118  54
         switch (aAST.getType()) {
 119  
         case TokenTypes.FOR_ITERATOR:
 120  9
             leaveForIter(aAST.getParent());
 121  9
             break;
 122  
         case TokenTypes.FOR_EACH_CLAUSE:
 123  2
             leaveForEach(aAST);
 124  2
             break;
 125  
         case TokenTypes.LITERAL_FOR:
 126  11
             leaveForDef(aAST);
 127  11
             break;
 128  
         case TokenTypes.OBJBLOCK:
 129  2
             exitBlock();
 130  2
             break;
 131  
         case TokenTypes.ASSIGN:
 132  
         case TokenTypes.PLUS_ASSIGN:
 133  
         case TokenTypes.MINUS_ASSIGN:
 134  
         case TokenTypes.STAR_ASSIGN:
 135  
         case TokenTypes.DIV_ASSIGN:
 136  
         case TokenTypes.MOD_ASSIGN:
 137  
         case TokenTypes.SR_ASSIGN:
 138  
         case TokenTypes.BSR_ASSIGN:
 139  
         case TokenTypes.SL_ASSIGN:
 140  
         case TokenTypes.BAND_ASSIGN:
 141  
         case TokenTypes.BXOR_ASSIGN:
 142  
         case TokenTypes.BOR_ASSIGN:
 143  
         case TokenTypes.INC:
 144  
         case TokenTypes.POST_INC:
 145  
         case TokenTypes.DEC:
 146  
         case TokenTypes.POST_DEC:
 147  
             // Do nothing
 148  30
             break;
 149  
         default:
 150  0
             throw new IllegalStateException(aAST.toString());
 151  
         }
 152  54
     }
 153  
 
 154  
     /**
 155  
      * Enters an inner class, which requires a new variable set.
 156  
      */
 157  
     private void enterBlock()
 158  
     {
 159  2
         mVariableStack.push(mCurrentVariables);
 160  2
         mCurrentVariables = FastStack.newInstance();
 161  
 
 162  2
     }
 163  
     /**
 164  
      * Leave an inner class, so restore variable set.
 165  
      */
 166  
     private void exitBlock()
 167  
     {
 168  2
         mCurrentVariables = mVariableStack.pop();
 169  2
     }
 170  
 
 171  
     /**
 172  
      * Check if ident is parameter.
 173  
      * @param aAST ident to check.
 174  
      */
 175  
     private void checkIdent(DetailAST aAST)
 176  
     {
 177  30
         if ((mCurrentVariables != null) && !mCurrentVariables.isEmpty()) {
 178  10
             final DetailAST identAST = aAST.getFirstChild();
 179  
 
 180  10
             if ((identAST != null)
 181  
                 && (identAST.getType() == TokenTypes.IDENT)
 182  
                 && mCurrentVariables.contains(identAST.getText()))
 183  
             {
 184  6
                 log(aAST.getLineNo(), aAST.getColumnNo(),
 185  
                     "modified.control.variable", identAST.getText());
 186  
             }
 187  
         }
 188  30
     }
 189  
 
 190  
     /**
 191  
      * Push current variables to the stack.
 192  
      * @param aAST a for definition.
 193  
      */
 194  
     private void leaveForIter(DetailAST aAST)
 195  
     {
 196  9
         final DetailAST forInitAST = aAST.findFirstToken(TokenTypes.FOR_INIT);
 197  9
         DetailAST parameterDefAST =
 198  
             forInitAST.findFirstToken(TokenTypes.VARIABLE_DEF);
 199  
 
 200  20
         for (; parameterDefAST != null;
 201  11
              parameterDefAST = parameterDefAST.getNextSibling())
 202  
         {
 203  11
             if (parameterDefAST.getType() == TokenTypes.VARIABLE_DEF) {
 204  10
                 final DetailAST param =
 205  
                     parameterDefAST.findFirstToken(TokenTypes.IDENT);
 206  10
                 mCurrentVariables.push(param.getText());
 207  
             }
 208  
         }
 209  9
     }
 210  
 
 211  
     /**
 212  
      * Push current variables to the stack.
 213  
      * @param aForEach a for-each clause
 214  
      */
 215  
     private void leaveForEach(DetailAST aForEach)
 216  
     {
 217  2
         final DetailAST paramDef =
 218  
             aForEach.findFirstToken(TokenTypes.VARIABLE_DEF);
 219  2
         final DetailAST paramName = paramDef.findFirstToken(TokenTypes.IDENT);
 220  2
         mCurrentVariables.push(paramName.getText());
 221  2
     }
 222  
 
 223  
     /**
 224  
      * Pops the variables from the stack.
 225  
      * @param aAST a for definition.
 226  
      */
 227  
     private void leaveForDef(DetailAST aAST)
 228  
     {
 229  11
         final DetailAST forInitAST = aAST.findFirstToken(TokenTypes.FOR_INIT);
 230  11
         if (forInitAST != null) {
 231  9
             DetailAST parameterDefAST =
 232  
                 forInitAST.findFirstToken(TokenTypes.VARIABLE_DEF);
 233  
 
 234  20
             for (; parameterDefAST != null;
 235  11
                  parameterDefAST = parameterDefAST.getNextSibling())
 236  
             {
 237  11
                 if (parameterDefAST.getType() == TokenTypes.VARIABLE_DEF) {
 238  10
                     mCurrentVariables.pop();
 239  
                 }
 240  
             }
 241  9
         }
 242  
         else {
 243  
             // this is for-each loop, just pop veriables
 244  2
             mCurrentVariables.pop();
 245  
         }
 246  11
     }
 247  
 }