001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2015 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.coding; 021 022import java.util.ArrayDeque; 023import java.util.Collections; 024import java.util.Deque; 025import java.util.Set; 026 027import com.google.common.collect.Sets; 028import com.puppycrawl.tools.checkstyle.api.Check; 029import com.puppycrawl.tools.checkstyle.api.DetailAST; 030import com.puppycrawl.tools.checkstyle.api.TokenTypes; 031 032/** 033 * <p> 034 * Disallow assignment of parameters. 035 * </p> 036 * <p> 037 * Rationale: 038 * Parameter assignment is often considered poor 039 * programming practice. Forcing developers to declare 040 * parameters as final is often onerous. Having a check 041 * ensure that parameters are never assigned would give 042 * the best of both worlds. 043 * </p> 044 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> 045 */ 046public final class ParameterAssignmentCheck extends Check { 047 048 /** 049 * A key is pointing to the warning message text in "messages.properties" 050 * file. 051 */ 052 public static final String MSG_KEY = "parameter.assignment"; 053 054 /** Stack of methods' parameters. */ 055 private final Deque<Set<String>> parameterNamesStack = new ArrayDeque<>(); 056 /** Current set of parameters. */ 057 private Set<String> parameterNames; 058 059 @Override 060 public int[] getDefaultTokens() { 061 return new int[] { 062 TokenTypes.CTOR_DEF, 063 TokenTypes.METHOD_DEF, 064 TokenTypes.ASSIGN, 065 TokenTypes.PLUS_ASSIGN, 066 TokenTypes.MINUS_ASSIGN, 067 TokenTypes.STAR_ASSIGN, 068 TokenTypes.DIV_ASSIGN, 069 TokenTypes.MOD_ASSIGN, 070 TokenTypes.SR_ASSIGN, 071 TokenTypes.BSR_ASSIGN, 072 TokenTypes.SL_ASSIGN, 073 TokenTypes.BAND_ASSIGN, 074 TokenTypes.BXOR_ASSIGN, 075 TokenTypes.BOR_ASSIGN, 076 TokenTypes.INC, 077 TokenTypes.POST_INC, 078 TokenTypes.DEC, 079 TokenTypes.POST_DEC, 080 }; 081 } 082 083 @Override 084 public int[] getRequiredTokens() { 085 return getDefaultTokens(); 086 } 087 088 @Override 089 public int[] getAcceptableTokens() { 090 return new int[] { 091 TokenTypes.CTOR_DEF, 092 TokenTypes.METHOD_DEF, 093 TokenTypes.ASSIGN, 094 TokenTypes.PLUS_ASSIGN, 095 TokenTypes.MINUS_ASSIGN, 096 TokenTypes.STAR_ASSIGN, 097 TokenTypes.DIV_ASSIGN, 098 TokenTypes.MOD_ASSIGN, 099 TokenTypes.SR_ASSIGN, 100 TokenTypes.BSR_ASSIGN, 101 TokenTypes.SL_ASSIGN, 102 TokenTypes.BAND_ASSIGN, 103 TokenTypes.BXOR_ASSIGN, 104 TokenTypes.BOR_ASSIGN, 105 TokenTypes.INC, 106 TokenTypes.POST_INC, 107 TokenTypes.DEC, 108 TokenTypes.POST_DEC, 109 }; 110 } 111 112 @Override 113 public void beginTree(DetailAST rootAST) { 114 // clear data 115 parameterNamesStack.clear(); 116 parameterNames = Collections.emptySet(); 117 } 118 119 @Override 120 public void visitToken(DetailAST ast) { 121 switch (ast.getType()) { 122 case TokenTypes.CTOR_DEF: 123 case TokenTypes.METHOD_DEF: 124 visitMethodDef(ast); 125 break; 126 case TokenTypes.ASSIGN: 127 case TokenTypes.PLUS_ASSIGN: 128 case TokenTypes.MINUS_ASSIGN: 129 case TokenTypes.STAR_ASSIGN: 130 case TokenTypes.DIV_ASSIGN: 131 case TokenTypes.MOD_ASSIGN: 132 case TokenTypes.SR_ASSIGN: 133 case TokenTypes.BSR_ASSIGN: 134 case TokenTypes.SL_ASSIGN: 135 case TokenTypes.BAND_ASSIGN: 136 case TokenTypes.BXOR_ASSIGN: 137 case TokenTypes.BOR_ASSIGN: 138 visitAssign(ast); 139 break; 140 case TokenTypes.INC: 141 case TokenTypes.POST_INC: 142 case TokenTypes.DEC: 143 case TokenTypes.POST_DEC: 144 visitIncDec(ast); 145 break; 146 default: 147 throw new IllegalStateException(ast.toString()); 148 } 149 } 150 151 @Override 152 public void leaveToken(DetailAST ast) { 153 switch (ast.getType()) { 154 case TokenTypes.CTOR_DEF: 155 case TokenTypes.METHOD_DEF: 156 leaveMethodDef(); 157 break; 158 case TokenTypes.ASSIGN: 159 case TokenTypes.PLUS_ASSIGN: 160 case TokenTypes.MINUS_ASSIGN: 161 case TokenTypes.STAR_ASSIGN: 162 case TokenTypes.DIV_ASSIGN: 163 case TokenTypes.MOD_ASSIGN: 164 case TokenTypes.SR_ASSIGN: 165 case TokenTypes.BSR_ASSIGN: 166 case TokenTypes.SL_ASSIGN: 167 case TokenTypes.BAND_ASSIGN: 168 case TokenTypes.BXOR_ASSIGN: 169 case TokenTypes.BOR_ASSIGN: 170 case TokenTypes.INC: 171 case TokenTypes.POST_INC: 172 case TokenTypes.DEC: 173 case TokenTypes.POST_DEC: 174 // Do nothing 175 break; 176 default: 177 throw new IllegalStateException(ast.toString()); 178 } 179 } 180 181 /** 182 * Checks if this is assignments of parameter. 183 * @param ast assignment to check. 184 */ 185 private void visitAssign(DetailAST ast) { 186 checkIdent(ast); 187 } 188 189 /** 190 * Checks if this is increment/decrement of parameter. 191 * @param ast dec/inc to check. 192 */ 193 private void visitIncDec(DetailAST ast) { 194 checkIdent(ast); 195 } 196 197 /** 198 * Check if ident is parameter. 199 * @param ast ident to check. 200 */ 201 private void checkIdent(DetailAST ast) { 202 if (!parameterNames.isEmpty()) { 203 final DetailAST identAST = ast.getFirstChild(); 204 205 if (identAST != null 206 && identAST.getType() == TokenTypes.IDENT 207 && parameterNames.contains(identAST.getText())) { 208 log(ast.getLineNo(), ast.getColumnNo(), 209 MSG_KEY, identAST.getText()); 210 } 211 } 212 } 213 214 /** 215 * Creates new set of parameters and store old one in stack. 216 * @param ast a method to process. 217 */ 218 private void visitMethodDef(DetailAST ast) { 219 parameterNamesStack.push(parameterNames); 220 parameterNames = Sets.newHashSet(); 221 222 visitMethodParameters(ast.findFirstToken(TokenTypes.PARAMETERS)); 223 } 224 225 /** Restores old set of parameters. */ 226 private void leaveMethodDef() { 227 parameterNames = parameterNamesStack.pop(); 228 } 229 230 /** 231 * Creates new parameter set for given method. 232 * @param ast a method for process. 233 */ 234 private void visitMethodParameters(DetailAST ast) { 235 DetailAST parameterDefAST = 236 ast.findFirstToken(TokenTypes.PARAMETER_DEF); 237 238 while (parameterDefAST != null) { 239 if (parameterDefAST.getType() == TokenTypes.PARAMETER_DEF) { 240 final DetailAST param = 241 parameterDefAST.findFirstToken(TokenTypes.IDENT); 242 parameterNames.add(param.getText()); 243 } 244 parameterDefAST = parameterDefAST.getNextSibling(); 245 } 246 } 247}