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}