View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.ArrayDeque;
23  import java.util.Deque;
24  import java.util.HashSet;
25  import java.util.Set;
26  
27  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
28  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
29  import com.puppycrawl.tools.checkstyle.api.DetailAST;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
32  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
33  
34  /**
35   * <div>
36   * Disallows assignment of parameters.
37   * </div>
38   *
39   * <p>
40   * Rationale:
41   * Parameter assignment is often considered poor
42   * programming practice. Forcing developers to declare
43   * parameters as final is often onerous. Having a check
44   * ensure that parameters are never assigned would give
45   * the best of both worlds.
46   * </p>
47   *
48   * @since 3.2
49   */
50  @FileStatefulCheck
51  public final class ParameterAssignmentCheck extends AbstractCheck {
52  
53      /**
54       * A key is pointing to the warning message text in "messages.properties"
55       * file.
56       */
57      public static final String MSG_KEY = "parameter.assignment";
58  
59      /** Stack of methods' parameters. */
60      private final Deque<Set<String>> parameterNamesStack = new ArrayDeque<>();
61      /** Current set of parameters. */
62      private Set<String> parameterNames;
63  
64      /**
65       * Creates a new {@code ParameterAssignmentCheck} instance.
66       */
67      public ParameterAssignmentCheck() {
68          // no code by default
69      }
70  
71      @Override
72      public int[] getDefaultTokens() {
73          return getRequiredTokens();
74      }
75  
76      @Override
77      public int[] getRequiredTokens() {
78          return new int[] {
79              TokenTypes.CTOR_DEF,
80              TokenTypes.METHOD_DEF,
81              TokenTypes.ASSIGN,
82              TokenTypes.PLUS_ASSIGN,
83              TokenTypes.MINUS_ASSIGN,
84              TokenTypes.STAR_ASSIGN,
85              TokenTypes.DIV_ASSIGN,
86              TokenTypes.MOD_ASSIGN,
87              TokenTypes.SR_ASSIGN,
88              TokenTypes.BSR_ASSIGN,
89              TokenTypes.SL_ASSIGN,
90              TokenTypes.BAND_ASSIGN,
91              TokenTypes.BXOR_ASSIGN,
92              TokenTypes.BOR_ASSIGN,
93              TokenTypes.INC,
94              TokenTypes.POST_INC,
95              TokenTypes.DEC,
96              TokenTypes.POST_DEC,
97              TokenTypes.LAMBDA,
98          };
99      }
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 }