View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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.Collections;
24  import java.util.Deque;
25  import java.util.HashSet;
26  import java.util.Set;
27  
28  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
29  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
30  import com.puppycrawl.tools.checkstyle.api.DetailAST;
31  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
32  import com.puppycrawl.tools.checkstyle.utils.CheckUtils;
33  
34  /**
35   * <p>
36   * Disallow assignment of parameters.
37   * </p>
38   * <p>
39   * Rationale:
40   * Parameter assignment is often considered poor
41   * programming practice. Forcing developers to declare
42   * parameters as final is often onerous. Having a check
43   * ensure that parameters are never assigned would give
44   * the best of both worlds.
45   * </p>
46   * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
47   */
48  @FileStatefulCheck
49  public final class ParameterAssignmentCheck extends AbstractCheck {
50  
51      /**
52       * A key is pointing to the warning message text in "messages.properties"
53       * file.
54       */
55      public static final String MSG_KEY = "parameter.assignment";
56  
57      /** Stack of methods' parameters. */
58      private final Deque<Set<String>> parameterNamesStack = new ArrayDeque<>();
59      /** Current set of parameters. */
60      private Set<String> parameterNames;
61  
62      @Override
63      public int[] getDefaultTokens() {
64          return getRequiredTokens();
65      }
66  
67      @Override
68      public int[] getRequiredTokens() {
69          return new int[] {
70              TokenTypes.CTOR_DEF,
71              TokenTypes.METHOD_DEF,
72              TokenTypes.ASSIGN,
73              TokenTypes.PLUS_ASSIGN,
74              TokenTypes.MINUS_ASSIGN,
75              TokenTypes.STAR_ASSIGN,
76              TokenTypes.DIV_ASSIGN,
77              TokenTypes.MOD_ASSIGN,
78              TokenTypes.SR_ASSIGN,
79              TokenTypes.BSR_ASSIGN,
80              TokenTypes.SL_ASSIGN,
81              TokenTypes.BAND_ASSIGN,
82              TokenTypes.BXOR_ASSIGN,
83              TokenTypes.BOR_ASSIGN,
84              TokenTypes.INC,
85              TokenTypes.POST_INC,
86              TokenTypes.DEC,
87              TokenTypes.POST_DEC,
88          };
89      }
90  
91      @Override
92      public int[] getAcceptableTokens() {
93          return getRequiredTokens();
94      }
95  
96      @Override
97      public void beginTree(DetailAST rootAST) {
98          // clear data
99          parameterNamesStack.clear();
100         parameterNames = Collections.emptySet();
101     }
102 
103     @Override
104     public void visitToken(DetailAST ast) {
105         switch (ast.getType()) {
106             case TokenTypes.CTOR_DEF:
107             case TokenTypes.METHOD_DEF:
108                 visitMethodDef(ast);
109                 break;
110             case TokenTypes.ASSIGN:
111             case TokenTypes.PLUS_ASSIGN:
112             case TokenTypes.MINUS_ASSIGN:
113             case TokenTypes.STAR_ASSIGN:
114             case TokenTypes.DIV_ASSIGN:
115             case TokenTypes.MOD_ASSIGN:
116             case TokenTypes.SR_ASSIGN:
117             case TokenTypes.BSR_ASSIGN:
118             case TokenTypes.SL_ASSIGN:
119             case TokenTypes.BAND_ASSIGN:
120             case TokenTypes.BXOR_ASSIGN:
121             case TokenTypes.BOR_ASSIGN:
122                 visitAssign(ast);
123                 break;
124             case TokenTypes.INC:
125             case TokenTypes.POST_INC:
126             case TokenTypes.DEC:
127             case TokenTypes.POST_DEC:
128                 visitIncDec(ast);
129                 break;
130             default:
131                 throw new IllegalStateException(ast.toString());
132         }
133     }
134 
135     @Override
136     public void leaveToken(DetailAST ast) {
137         switch (ast.getType()) {
138             case TokenTypes.CTOR_DEF:
139             case TokenTypes.METHOD_DEF:
140                 leaveMethodDef();
141                 break;
142             case TokenTypes.ASSIGN:
143             case TokenTypes.PLUS_ASSIGN:
144             case TokenTypes.MINUS_ASSIGN:
145             case TokenTypes.STAR_ASSIGN:
146             case TokenTypes.DIV_ASSIGN:
147             case TokenTypes.MOD_ASSIGN:
148             case TokenTypes.SR_ASSIGN:
149             case TokenTypes.BSR_ASSIGN:
150             case TokenTypes.SL_ASSIGN:
151             case TokenTypes.BAND_ASSIGN:
152             case TokenTypes.BXOR_ASSIGN:
153             case TokenTypes.BOR_ASSIGN:
154             case TokenTypes.INC:
155             case TokenTypes.POST_INC:
156             case TokenTypes.DEC:
157             case TokenTypes.POST_DEC:
158                 // Do nothing
159                 break;
160             default:
161                 throw new IllegalStateException(ast.toString());
162         }
163     }
164 
165     /**
166      * Checks if this is assignments of parameter.
167      * @param ast assignment to check.
168      */
169     private void visitAssign(DetailAST ast) {
170         checkIdent(ast);
171     }
172 
173     /**
174      * Checks if this is increment/decrement of parameter.
175      * @param ast dec/inc to check.
176      */
177     private void visitIncDec(DetailAST ast) {
178         checkIdent(ast);
179     }
180 
181     /**
182      * Check if ident is parameter.
183      * @param ast ident to check.
184      */
185     private void checkIdent(DetailAST ast) {
186         if (!parameterNames.isEmpty()) {
187             final DetailAST identAST = ast.getFirstChild();
188 
189             if (identAST != null
190                 && identAST.getType() == TokenTypes.IDENT
191                 && parameterNames.contains(identAST.getText())) {
192                 log(ast.getLineNo(), ast.getColumnNo(),
193                     MSG_KEY, identAST.getText());
194             }
195         }
196     }
197 
198     /**
199      * Creates new set of parameters and store old one in stack.
200      * @param ast a method to process.
201      */
202     private void visitMethodDef(DetailAST ast) {
203         parameterNamesStack.push(parameterNames);
204         parameterNames = new HashSet<>();
205 
206         visitMethodParameters(ast.findFirstToken(TokenTypes.PARAMETERS));
207     }
208 
209     /** Restores old set of parameters. */
210     private void leaveMethodDef() {
211         parameterNames = parameterNamesStack.pop();
212     }
213 
214     /**
215      * Creates new parameter set for given method.
216      * @param ast a method for process.
217      */
218     private void visitMethodParameters(DetailAST ast) {
219         DetailAST parameterDefAST =
220             ast.findFirstToken(TokenTypes.PARAMETER_DEF);
221 
222         while (parameterDefAST != null) {
223             if (parameterDefAST.getType() == TokenTypes.PARAMETER_DEF
224                     && !CheckUtils.isReceiverParameter(parameterDefAST)) {
225                 final DetailAST param =
226                     parameterDefAST.findFirstToken(TokenTypes.IDENT);
227                 parameterNames.add(param.getText());
228             }
229             parameterDefAST = parameterDefAST.getNextSibling();
230         }
231     }
232 }