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