001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2025 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.FileStatefulCheck; 029import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 030import com.puppycrawl.tools.checkstyle.api.DetailAST; 031import com.puppycrawl.tools.checkstyle.api.TokenTypes; 032import com.puppycrawl.tools.checkstyle.utils.CheckUtil; 033import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 034 035/** 036 * <div> 037 * Disallows assignment of parameters. 038 * </div> 039 * 040 * <p> 041 * Rationale: 042 * Parameter assignment is often considered poor 043 * programming practice. Forcing developers to declare 044 * parameters as final is often onerous. Having a check 045 * ensure that parameters are never assigned would give 046 * the best of both worlds. 047 * </p> 048 * 049 * @since 3.2 050 */ 051@FileStatefulCheck 052public final class ParameterAssignmentCheck extends AbstractCheck { 053 054 /** 055 * A key is pointing to the warning message text in "messages.properties" 056 * file. 057 */ 058 public static final String MSG_KEY = "parameter.assignment"; 059 060 /** Stack of methods' parameters. */ 061 private final Deque<Set<String>> parameterNamesStack = new ArrayDeque<>(); 062 /** Current set of parameters. */ 063 private Set<String> parameterNames; 064 065 @Override 066 public int[] getDefaultTokens() { 067 return getRequiredTokens(); 068 } 069 070 @Override 071 public int[] getRequiredTokens() { 072 return new int[] { 073 TokenTypes.CTOR_DEF, 074 TokenTypes.METHOD_DEF, 075 TokenTypes.ASSIGN, 076 TokenTypes.PLUS_ASSIGN, 077 TokenTypes.MINUS_ASSIGN, 078 TokenTypes.STAR_ASSIGN, 079 TokenTypes.DIV_ASSIGN, 080 TokenTypes.MOD_ASSIGN, 081 TokenTypes.SR_ASSIGN, 082 TokenTypes.BSR_ASSIGN, 083 TokenTypes.SL_ASSIGN, 084 TokenTypes.BAND_ASSIGN, 085 TokenTypes.BXOR_ASSIGN, 086 TokenTypes.BOR_ASSIGN, 087 TokenTypes.INC, 088 TokenTypes.POST_INC, 089 TokenTypes.DEC, 090 TokenTypes.POST_DEC, 091 TokenTypes.LAMBDA, 092 }; 093 } 094 095 @Override 096 public int[] getAcceptableTokens() { 097 return getRequiredTokens(); 098 } 099 100 @Override 101 public void beginTree(DetailAST rootAST) { 102 // clear data 103 parameterNamesStack.clear(); 104 parameterNames = Collections.emptySet(); 105 } 106 107 @Override 108 public void visitToken(DetailAST ast) { 109 final int type = ast.getType(); 110 if (TokenUtil.isOfType(type, TokenTypes.CTOR_DEF, TokenTypes.METHOD_DEF)) { 111 visitMethodDef(ast); 112 } 113 else if (type == TokenTypes.LAMBDA) { 114 if (ast.getParent().getType() != TokenTypes.SWITCH_RULE) { 115 visitLambda(ast); 116 } 117 } 118 else { 119 checkNestedIdent(ast); 120 } 121 } 122 123 @Override 124 public void leaveToken(DetailAST ast) { 125 final int type = ast.getType(); 126 if (TokenUtil.isOfType(type, TokenTypes.CTOR_DEF, TokenTypes.METHOD_DEF) 127 || type == TokenTypes.LAMBDA 128 && ast.getParent().getType() != TokenTypes.SWITCH_RULE) { 129 parameterNames = parameterNamesStack.pop(); 130 } 131 } 132 133 /** 134 * Check if nested ident is parameter. 135 * 136 * @param ast parent of node of ident 137 */ 138 private void checkNestedIdent(DetailAST ast) { 139 final DetailAST identAST = ast.getFirstChild(); 140 141 if (identAST != null 142 && identAST.getType() == TokenTypes.IDENT 143 && parameterNames.contains(identAST.getText())) { 144 log(ast, MSG_KEY, identAST.getText()); 145 } 146 } 147 148 /** 149 * Creates new set of parameters and store old one in stack. 150 * 151 * @param ast a method to process. 152 */ 153 private void visitMethodDef(DetailAST ast) { 154 parameterNamesStack.push(parameterNames); 155 parameterNames = new HashSet<>(); 156 157 visitMethodParameters(ast.findFirstToken(TokenTypes.PARAMETERS)); 158 } 159 160 /** 161 * Creates new set of parameters and store old one in stack. 162 * 163 * @param lambdaAst node of type {@link TokenTypes#LAMBDA}. 164 */ 165 private void visitLambda(DetailAST lambdaAst) { 166 parameterNamesStack.push(parameterNames); 167 parameterNames = new HashSet<>(); 168 169 DetailAST parameterAst = lambdaAst.findFirstToken(TokenTypes.PARAMETERS); 170 if (parameterAst == null) { 171 parameterAst = lambdaAst.getFirstChild(); 172 } 173 visitLambdaParameters(parameterAst); 174 } 175 176 /** 177 * Creates new parameter set for given method. 178 * 179 * @param ast a method for process. 180 */ 181 private void visitMethodParameters(DetailAST ast) { 182 visitParameters(ast); 183 } 184 185 /** 186 * Creates new parameter set for given lambda expression. 187 * 188 * @param ast a lambda expression parameter to process 189 */ 190 private void visitLambdaParameters(DetailAST ast) { 191 if (ast.getType() == TokenTypes.IDENT) { 192 parameterNames.add(ast.getText()); 193 } 194 else { 195 visitParameters(ast); 196 } 197 } 198 199 /** 200 * Visits parameter list and adds parameter names to the set. 201 * 202 * @param parametersAst ast node of type {@link TokenTypes#PARAMETERS}. 203 */ 204 private void visitParameters(DetailAST parametersAst) { 205 DetailAST parameterDefAST = 206 parametersAst.findFirstToken(TokenTypes.PARAMETER_DEF); 207 208 while (parameterDefAST != null) { 209 if (!CheckUtil.isReceiverParameter(parameterDefAST)) { 210 final DetailAST param = 211 parameterDefAST.findFirstToken(TokenTypes.IDENT); 212 parameterNames.add(param.getText()); 213 } 214 parameterDefAST = parameterDefAST.getNextSibling(); 215 } 216 } 217 218}