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