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