001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2014  Oliver Burn
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////////////////////////////////////////////////////////////////////////////////
019package com.puppycrawl.tools.checkstyle.checks.coding;
020
021import com.google.common.collect.Sets;
022import com.puppycrawl.tools.checkstyle.api.Check;
023import com.puppycrawl.tools.checkstyle.api.DetailAST;
024import com.puppycrawl.tools.checkstyle.api.FastStack;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import java.util.Set;
027
028/**
029 * <p>
030 * Disallow assignment of parameters.
031 * </p>
032 * <p>
033 * Rationale:
034 * Parameter assignment is often considered poor
035 * programming practice. Forcing developers to declare
036 * parameters as final is often onerous. Having a check
037 * ensure that parameters are never assigned would give
038 * the best of both worlds.
039 * </p>
040 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
041 */
042public final class ParameterAssignmentCheck extends Check
043{
044    /** Stack of methods' parameters. */
045    private final FastStack<Set<String>> parameterNamesStack =
046        FastStack.newInstance();
047    /** Current set of perameters. */
048    private Set<String> parameterNames;
049
050    @Override
051    public int[] getDefaultTokens()
052    {
053        return new int[] {
054            TokenTypes.CTOR_DEF,
055            TokenTypes.METHOD_DEF,
056            TokenTypes.ASSIGN,
057            TokenTypes.PLUS_ASSIGN,
058            TokenTypes.MINUS_ASSIGN,
059            TokenTypes.STAR_ASSIGN,
060            TokenTypes.DIV_ASSIGN,
061            TokenTypes.MOD_ASSIGN,
062            TokenTypes.SR_ASSIGN,
063            TokenTypes.BSR_ASSIGN,
064            TokenTypes.SL_ASSIGN,
065            TokenTypes.BAND_ASSIGN,
066            TokenTypes.BXOR_ASSIGN,
067            TokenTypes.BOR_ASSIGN,
068            TokenTypes.INC,
069            TokenTypes.POST_INC,
070            TokenTypes.DEC,
071            TokenTypes.POST_DEC,
072        };
073    }
074
075    @Override
076    public int[] getRequiredTokens()
077    {
078        return getDefaultTokens();
079    }
080
081    @Override
082    public void beginTree(DetailAST rootAST)
083    {
084        // clear data
085        parameterNamesStack.clear();
086        parameterNames = null;
087    }
088
089    @Override
090    public void visitToken(DetailAST ast)
091    {
092        switch (ast.getType()) {
093            case TokenTypes.CTOR_DEF:
094            case TokenTypes.METHOD_DEF:
095                visitMethodDef(ast);
096                break;
097            case TokenTypes.ASSIGN:
098            case TokenTypes.PLUS_ASSIGN:
099            case TokenTypes.MINUS_ASSIGN:
100            case TokenTypes.STAR_ASSIGN:
101            case TokenTypes.DIV_ASSIGN:
102            case TokenTypes.MOD_ASSIGN:
103            case TokenTypes.SR_ASSIGN:
104            case TokenTypes.BSR_ASSIGN:
105            case TokenTypes.SL_ASSIGN:
106            case TokenTypes.BAND_ASSIGN:
107            case TokenTypes.BXOR_ASSIGN:
108            case TokenTypes.BOR_ASSIGN:
109                visitAssign(ast);
110                break;
111            case TokenTypes.INC:
112            case TokenTypes.POST_INC:
113            case TokenTypes.DEC:
114            case TokenTypes.POST_DEC:
115                visitIncDec(ast);
116                break;
117            default:
118                throw new IllegalStateException(ast.toString());
119        }
120    }
121
122    @Override
123    public void leaveToken(DetailAST ast)
124    {
125        switch (ast.getType()) {
126            case TokenTypes.CTOR_DEF:
127            case TokenTypes.METHOD_DEF:
128                leaveMethodDef();
129                break;
130            case TokenTypes.ASSIGN:
131            case TokenTypes.PLUS_ASSIGN:
132            case TokenTypes.MINUS_ASSIGN:
133            case TokenTypes.STAR_ASSIGN:
134            case TokenTypes.DIV_ASSIGN:
135            case TokenTypes.MOD_ASSIGN:
136            case TokenTypes.SR_ASSIGN:
137            case TokenTypes.BSR_ASSIGN:
138            case TokenTypes.SL_ASSIGN:
139            case TokenTypes.BAND_ASSIGN:
140            case TokenTypes.BXOR_ASSIGN:
141            case TokenTypes.BOR_ASSIGN:
142            case TokenTypes.INC:
143            case TokenTypes.POST_INC:
144            case TokenTypes.DEC:
145            case TokenTypes.POST_DEC:
146                // Do nothing
147                break;
148            default:
149                throw new IllegalStateException(ast.toString());
150        }
151    }
152
153    /**
154     * Ckecks if this is assignments of parameter.
155     * @param ast assignment to check.
156     */
157    private void visitAssign(DetailAST ast)
158    {
159        checkIdent(ast);
160    }
161
162    /**
163     * Checks if this is increment/decrement of parameter.
164     * @param ast dec/inc to check.
165     */
166    private void visitIncDec(DetailAST ast)
167    {
168        checkIdent(ast);
169    }
170
171    /**
172     * Check if ident is parameter.
173     * @param ast ident to check.
174     */
175    private void checkIdent(DetailAST ast)
176    {
177        if ((parameterNames != null) && !parameterNames.isEmpty()) {
178            final DetailAST identAST = ast.getFirstChild();
179
180            if ((identAST != null)
181                && (identAST.getType() == TokenTypes.IDENT)
182                && parameterNames.contains(identAST.getText()))
183            {
184                log(ast.getLineNo(), ast.getColumnNo(),
185                    "parameter.assignment", identAST.getText());
186            }
187        }
188    }
189
190    /**
191     * Creates new set of parameters and store old one in stack.
192     * @param ast a method to process.
193     */
194    private void visitMethodDef(DetailAST ast)
195    {
196        parameterNamesStack.push(parameterNames);
197        parameterNames = Sets.newHashSet();
198
199        visitMethodParameters(ast.findFirstToken(TokenTypes.PARAMETERS));
200    }
201
202    /** Restores old set of parameters. */
203    private void leaveMethodDef()
204    {
205        parameterNames = parameterNamesStack.pop();
206    }
207
208    /**
209     * Creates new parameter set for given method.
210     * @param ast a method for process.
211     */
212    private void visitMethodParameters(DetailAST ast)
213    {
214        DetailAST parameterDefAST =
215            ast.findFirstToken(TokenTypes.PARAMETER_DEF);
216
217        for (; parameterDefAST != null;
218             parameterDefAST = parameterDefAST.getNextSibling())
219        {
220            if (parameterDefAST.getType() == TokenTypes.PARAMETER_DEF) {
221                final DetailAST param =
222                    parameterDefAST.findFirstToken(TokenTypes.IDENT);
223                parameterNames.add(param.getText());
224            }
225        }
226    }
227}