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 "this" default. 030 * That is references to instance variables and methods of the present 031 * object are explicitly of the form "this.varName" or 032 * "this.methodName(args)". 033 *</p> 034 * 035 * <p>Examples of use: 036 * <pre> 037 * <module name="RequireThis"/> 038 * </pre> 039 * An example of how to configure to check {@code this} qualifier for 040 * methods only: 041 * <pre> 042 * <module name="RequireThis"> 043 * <property name="checkFields" value="false"/> 044 * <property name="checkMethods" value="true"/> 045 * </module> 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}