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}