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