001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2015 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 com.google.common.collect.ImmutableSet;
023import com.puppycrawl.tools.checkstyle.api.DetailAST;
024import com.puppycrawl.tools.checkstyle.api.TokenTypes;
025import com.puppycrawl.tools.checkstyle.checks.AbstractDeclarationCollector;
026import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
027
028/**
029 * <p>Checks that code doesn't rely on the &quot;this&quot; default.
030 * That is references to instance variables and methods of the present
031 * object are explicitly of the form &quot;this.varName&quot; or
032 * &quot;this.methodName(args)&quot;.
033 *</p>
034 *
035 * <p>Examples of use:
036 * <pre>
037 * &lt;module name=&quot;RequireThis&quot;/&gt;
038 * </pre>
039 * An example of how to configure to check {@code this} qualifier for
040 * methods only:
041 * <pre>
042 * &lt;module name=&quot;RequireThis&quot;&gt;
043 *   &lt;property name=&quot;checkFields&quot; value=&quot;false&quot;/&gt;
044 *   &lt;property name=&quot;checkMethods&quot; value=&quot;true&quot;/&gt;
045 * &lt;/module&gt;
046 * </pre>
047 *
048 * <p>Limitations: I'm not currently doing anything about static variables
049 * or catch-blocks.  Static methods invoked on a class name seem to be OK;
050 * both the class name and the method name have a DOT parent.
051 * Non-static methods invoked on either this or a variable name seem to be
052 * OK, likewise.</p>
053 * <p>Much of the code for this check was cribbed from Rick Giles's
054 * {@code HiddenFieldCheck}.</p>
055 *
056 * @author Stephen Bloch
057 * @author o_sukhodolsky
058 */
059public class RequireThisCheck extends AbstractDeclarationCollector {
060
061    /**
062     * A key is pointing to the warning message text in "messages.properties"
063     * file.
064     */
065    public static final String MSG_METHOD = "require.this.method";
066
067    /**
068     * A key is pointing to the warning message text in "messages.properties"
069     * file.
070     */
071    public static final String MSG_VARIABLE = "require.this.variable";
072
073    /**
074     * Set of all declaration tokens.
075     */
076    private static final ImmutableSet<Integer> DECLARATION_TOKENS = ImmutableSet.of(
077            TokenTypes.VARIABLE_DEF,
078            TokenTypes.CTOR_DEF,
079            TokenTypes.METHOD_DEF,
080            TokenTypes.CLASS_DEF,
081            TokenTypes.ENUM_DEF,
082            TokenTypes.INTERFACE_DEF,
083            TokenTypes.PARAMETER_DEF,
084            TokenTypes.TYPE_ARGUMENT
085    );
086
087    /** Whether we should check fields usage. */
088    private boolean checkFields = true;
089    /** Whether we should check methods usage. */
090    private boolean checkMethods = true;
091
092    /**
093     * Setter for checkFields property.
094     * @param checkFields should we check fields usage or not.
095     */
096    public void setCheckFields(boolean checkFields) {
097        this.checkFields = checkFields;
098    }
099
100    /**
101     * Setter for checkMethods property.
102     * @param checkMethods should we check methods usage or not.
103     */
104    public void setCheckMethods(boolean checkMethods) {
105        this.checkMethods = checkMethods;
106    }
107
108    @Override
109    public int[] getDefaultTokens() {
110        return getAcceptableTokens();
111    }
112
113    @Override
114    public int[] getRequiredTokens() {
115        return getAcceptableTokens();
116    }
117
118    @Override
119    public int[] getAcceptableTokens() {
120        return new int[] {
121            TokenTypes.CLASS_DEF,
122            TokenTypes.INTERFACE_DEF,
123            TokenTypes.ENUM_DEF,
124            TokenTypes.CTOR_DEF,
125            TokenTypes.METHOD_DEF,
126            TokenTypes.SLIST,
127            TokenTypes.IDENT,
128        };
129    }
130
131    @Override
132    public void visitToken(DetailAST ast) {
133        super.visitToken(ast);
134        if (ast.getType() == TokenTypes.IDENT) {
135            processIdent(ast);
136        }
137    }
138
139    /**
140     * Checks if a given IDENT is method call or field name which
141     * require explicit {@code this} qualifier.
142     *
143     * @param ast IDENT to check.
144     */
145    private void processIdent(DetailAST ast) {
146        final int parentType = ast.getParent().getType();
147        switch (parentType) {
148            case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR:
149            case TokenTypes.ANNOTATION:
150            case TokenTypes.ANNOTATION_FIELD_DEF:
151                // no need to check annotations content
152                break;
153            case TokenTypes.METHOD_CALL:
154                // let's check method calls
155                if (checkMethods && isClassMethod(ast.getText())) {
156                    log(ast, MSG_METHOD, ast.getText());
157                }
158                break;
159            default:
160                if (checkFields) {
161                    processField(ast, parentType);
162                }
163                break;
164        }
165    }
166
167    /**
168     * Process validation of Field.
169     * @param ast field definition ast token
170     * @param parentType type of the parent
171     */
172    private void processField(DetailAST ast, int parentType) {
173        final boolean importOrPackage = ScopeUtils.getSurroundingScope(ast) == null;
174        final boolean methodNameInMethodCall = parentType == TokenTypes.DOT
175                && ast.getPreviousSibling() != null;
176        final boolean typeName = parentType == TokenTypes.TYPE
177                || parentType == TokenTypes.LITERAL_NEW;
178
179        if (!importOrPackage
180                && !methodNameInMethodCall
181                && !typeName
182                && !isDeclarationToken(parentType)) {
183
184            final String name = ast.getText();
185
186            if (isClassField(name)) {
187                log(ast, MSG_VARIABLE, name);
188            }
189        }
190    }
191
192    /**
193     * Check that token is related to Definition tokens.
194     * @param parentType token Type
195     * @return true if token is related to Definition Tokens
196     */
197    private static boolean isDeclarationToken(int parentType) {
198        return DECLARATION_TOKENS.contains(parentType);
199    }
200}