001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 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.HashSet; 026import java.util.Set; 027 028import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 029import com.puppycrawl.tools.checkstyle.api.DetailAST; 030import com.puppycrawl.tools.checkstyle.api.TokenTypes; 031import com.puppycrawl.tools.checkstyle.utils.CheckUtils; 032 033/** 034 * <p> 035 * Disallow assignment of parameters. 036 * </p> 037 * <p> 038 * Rationale: 039 * Parameter assignment is often considered poor 040 * programming practice. Forcing developers to declare 041 * parameters as final is often onerous. Having a check 042 * ensure that parameters are never assigned would give 043 * the best of both worlds. 044 * </p> 045 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> 046 */ 047public final class ParameterAssignmentCheck extends AbstractCheck { 048 049 /** 050 * A key is pointing to the warning message text in "messages.properties" 051 * file. 052 */ 053 public static final String MSG_KEY = "parameter.assignment"; 054 055 /** Stack of methods' parameters. */ 056 private final Deque<Set<String>> parameterNamesStack = new ArrayDeque<>(); 057 /** Current set of parameters. */ 058 private Set<String> parameterNames; 059 060 @Override 061 public int[] getDefaultTokens() { 062 return new int[] { 063 TokenTypes.CTOR_DEF, 064 TokenTypes.METHOD_DEF, 065 TokenTypes.ASSIGN, 066 TokenTypes.PLUS_ASSIGN, 067 TokenTypes.MINUS_ASSIGN, 068 TokenTypes.STAR_ASSIGN, 069 TokenTypes.DIV_ASSIGN, 070 TokenTypes.MOD_ASSIGN, 071 TokenTypes.SR_ASSIGN, 072 TokenTypes.BSR_ASSIGN, 073 TokenTypes.SL_ASSIGN, 074 TokenTypes.BAND_ASSIGN, 075 TokenTypes.BXOR_ASSIGN, 076 TokenTypes.BOR_ASSIGN, 077 TokenTypes.INC, 078 TokenTypes.POST_INC, 079 TokenTypes.DEC, 080 TokenTypes.POST_DEC, 081 }; 082 } 083 084 @Override 085 public int[] getRequiredTokens() { 086 return getDefaultTokens(); 087 } 088 089 @Override 090 public int[] getAcceptableTokens() { 091 return new int[] { 092 TokenTypes.CTOR_DEF, 093 TokenTypes.METHOD_DEF, 094 TokenTypes.ASSIGN, 095 TokenTypes.PLUS_ASSIGN, 096 TokenTypes.MINUS_ASSIGN, 097 TokenTypes.STAR_ASSIGN, 098 TokenTypes.DIV_ASSIGN, 099 TokenTypes.MOD_ASSIGN, 100 TokenTypes.SR_ASSIGN, 101 TokenTypes.BSR_ASSIGN, 102 TokenTypes.SL_ASSIGN, 103 TokenTypes.BAND_ASSIGN, 104 TokenTypes.BXOR_ASSIGN, 105 TokenTypes.BOR_ASSIGN, 106 TokenTypes.INC, 107 TokenTypes.POST_INC, 108 TokenTypes.DEC, 109 TokenTypes.POST_DEC, 110 }; 111 } 112 113 @Override 114 public void beginTree(DetailAST rootAST) { 115 // clear data 116 parameterNamesStack.clear(); 117 parameterNames = Collections.emptySet(); 118 } 119 120 @Override 121 public void visitToken(DetailAST ast) { 122 switch (ast.getType()) { 123 case TokenTypes.CTOR_DEF: 124 case TokenTypes.METHOD_DEF: 125 visitMethodDef(ast); 126 break; 127 case TokenTypes.ASSIGN: 128 case TokenTypes.PLUS_ASSIGN: 129 case TokenTypes.MINUS_ASSIGN: 130 case TokenTypes.STAR_ASSIGN: 131 case TokenTypes.DIV_ASSIGN: 132 case TokenTypes.MOD_ASSIGN: 133 case TokenTypes.SR_ASSIGN: 134 case TokenTypes.BSR_ASSIGN: 135 case TokenTypes.SL_ASSIGN: 136 case TokenTypes.BAND_ASSIGN: 137 case TokenTypes.BXOR_ASSIGN: 138 case TokenTypes.BOR_ASSIGN: 139 visitAssign(ast); 140 break; 141 case TokenTypes.INC: 142 case TokenTypes.POST_INC: 143 case TokenTypes.DEC: 144 case TokenTypes.POST_DEC: 145 visitIncDec(ast); 146 break; 147 default: 148 throw new IllegalStateException(ast.toString()); 149 } 150 } 151 152 @Override 153 public void leaveToken(DetailAST ast) { 154 switch (ast.getType()) { 155 case TokenTypes.CTOR_DEF: 156 case TokenTypes.METHOD_DEF: 157 leaveMethodDef(); 158 break; 159 case TokenTypes.ASSIGN: 160 case TokenTypes.PLUS_ASSIGN: 161 case TokenTypes.MINUS_ASSIGN: 162 case TokenTypes.STAR_ASSIGN: 163 case TokenTypes.DIV_ASSIGN: 164 case TokenTypes.MOD_ASSIGN: 165 case TokenTypes.SR_ASSIGN: 166 case TokenTypes.BSR_ASSIGN: 167 case TokenTypes.SL_ASSIGN: 168 case TokenTypes.BAND_ASSIGN: 169 case TokenTypes.BXOR_ASSIGN: 170 case TokenTypes.BOR_ASSIGN: 171 case TokenTypes.INC: 172 case TokenTypes.POST_INC: 173 case TokenTypes.DEC: 174 case TokenTypes.POST_DEC: 175 // Do nothing 176 break; 177 default: 178 throw new IllegalStateException(ast.toString()); 179 } 180 } 181 182 /** 183 * Checks if this is assignments of parameter. 184 * @param ast assignment to check. 185 */ 186 private void visitAssign(DetailAST ast) { 187 checkIdent(ast); 188 } 189 190 /** 191 * Checks if this is increment/decrement of parameter. 192 * @param ast dec/inc to check. 193 */ 194 private void visitIncDec(DetailAST ast) { 195 checkIdent(ast); 196 } 197 198 /** 199 * Check if ident is parameter. 200 * @param ast ident to check. 201 */ 202 private void checkIdent(DetailAST ast) { 203 if (!parameterNames.isEmpty()) { 204 final DetailAST identAST = ast.getFirstChild(); 205 206 if (identAST != null 207 && identAST.getType() == TokenTypes.IDENT 208 && parameterNames.contains(identAST.getText())) { 209 log(ast.getLineNo(), ast.getColumnNo(), 210 MSG_KEY, identAST.getText()); 211 } 212 } 213 } 214 215 /** 216 * Creates new set of parameters and store old one in stack. 217 * @param ast a method to process. 218 */ 219 private void visitMethodDef(DetailAST ast) { 220 parameterNamesStack.push(parameterNames); 221 parameterNames = new HashSet<>(); 222 223 visitMethodParameters(ast.findFirstToken(TokenTypes.PARAMETERS)); 224 } 225 226 /** Restores old set of parameters. */ 227 private void leaveMethodDef() { 228 parameterNames = parameterNamesStack.pop(); 229 } 230 231 /** 232 * Creates new parameter set for given method. 233 * @param ast a method for process. 234 */ 235 private void visitMethodParameters(DetailAST ast) { 236 DetailAST parameterDefAST = 237 ast.findFirstToken(TokenTypes.PARAMETER_DEF); 238 239 while (parameterDefAST != null) { 240 if (parameterDefAST.getType() == TokenTypes.PARAMETER_DEF 241 && !CheckUtils.isReceiverParameter(parameterDefAST)) { 242 final DetailAST param = 243 parameterDefAST.findFirstToken(TokenTypes.IDENT); 244 parameterNames.add(param.getText()); 245 } 246 parameterDefAST = parameterDefAST.getNextSibling(); 247 } 248 } 249}