001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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; 021 022import java.util.Arrays; 023import java.util.Collections; 024import java.util.Set; 025import java.util.stream.Collectors; 026 027import com.puppycrawl.tools.checkstyle.StatelessCheck; 028import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 029import com.puppycrawl.tools.checkstyle.api.DetailAST; 030import com.puppycrawl.tools.checkstyle.api.TokenTypes; 031import com.puppycrawl.tools.checkstyle.utils.CheckUtil; 032import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 033import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 034 035/** 036 * <p> 037 * Checks that parameters for methods, constructors, catch and for-each blocks are final. 038 * Interface, abstract, and native methods are not checked: the final keyword 039 * does not make sense for interface, abstract, and native method parameters as 040 * there is no code that could modify the parameter. 041 * </p> 042 * <p> 043 * Rationale: Changing the value of parameters during the execution of the method's 044 * algorithm can be confusing and should be avoided. A great way to let the Java compiler 045 * prevent this coding style is to declare parameters final. 046 * </p> 047 * <ul> 048 * <li> 049 * Property {@code ignorePrimitiveTypes} - Ignore primitive types as parameters. 050 * Type is {@code boolean}. 051 * Default value is {@code false}. 052 * </li> 053 * <li> 054 * Property {@code tokens} - tokens to check 055 * Type is {@code java.lang.String[]}. 056 * Validation type is {@code tokenSet}. 057 * Default value is: 058 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 059 * METHOD_DEF</a>, 060 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 061 * CTOR_DEF</a>. 062 * </li> 063 * </ul> 064 * <p> 065 * To configure the check to enforce final parameters for methods and constructors: 066 * </p> 067 * <pre> 068 * <module name="FinalParameters"/> 069 * </pre> 070 * <p> 071 * To configure the check to enforce final parameters only for constructors: 072 * </p> 073 * <pre> 074 * <module name="FinalParameters"> 075 * <property name="tokens" value="CTOR_DEF"/> 076 * </module> 077 * </pre> 078 * <p> 079 * To configure the check to allow ignoring 080 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html"> 081 * primitive datatypes</a> as parameters: 082 * </p> 083 * <pre> 084 * <module name="FinalParameters"> 085 * <property name="ignorePrimitiveTypes" value="true"/> 086 * </module> 087 * </pre> 088 * <p> 089 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 090 * </p> 091 * <p> 092 * Violation Message Keys: 093 * </p> 094 * <ul> 095 * <li> 096 * {@code final.parameter} 097 * </li> 098 * </ul> 099 * 100 * @since 3.0 101 */ 102@StatelessCheck 103public class FinalParametersCheck extends AbstractCheck { 104 105 /** 106 * A key is pointing to the warning message text in "messages.properties" 107 * file. 108 */ 109 public static final String MSG_KEY = "final.parameter"; 110 111 /** 112 * Contains 113 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html"> 114 * primitive datatypes</a>. 115 */ 116 private final Set<Integer> primitiveDataTypes = Collections.unmodifiableSet( 117 Arrays.stream(new Integer[] { 118 TokenTypes.LITERAL_BYTE, 119 TokenTypes.LITERAL_SHORT, 120 TokenTypes.LITERAL_INT, 121 TokenTypes.LITERAL_LONG, 122 TokenTypes.LITERAL_FLOAT, 123 TokenTypes.LITERAL_DOUBLE, 124 TokenTypes.LITERAL_BOOLEAN, 125 TokenTypes.LITERAL_CHAR, }) 126 .collect(Collectors.toSet())); 127 128 /** 129 * Ignore primitive types as parameters. 130 */ 131 private boolean ignorePrimitiveTypes; 132 133 /** 134 * Setter to ignore primitive types as parameters. 135 * 136 * @param ignorePrimitiveTypes true or false. 137 */ 138 public void setIgnorePrimitiveTypes(boolean ignorePrimitiveTypes) { 139 this.ignorePrimitiveTypes = ignorePrimitiveTypes; 140 } 141 142 @Override 143 public int[] getDefaultTokens() { 144 return new int[] { 145 TokenTypes.METHOD_DEF, 146 TokenTypes.CTOR_DEF, 147 }; 148 } 149 150 @Override 151 public int[] getAcceptableTokens() { 152 return new int[] { 153 TokenTypes.METHOD_DEF, 154 TokenTypes.CTOR_DEF, 155 TokenTypes.LITERAL_CATCH, 156 TokenTypes.FOR_EACH_CLAUSE, 157 }; 158 } 159 160 @Override 161 public int[] getRequiredTokens() { 162 return CommonUtil.EMPTY_INT_ARRAY; 163 } 164 165 @Override 166 public void visitToken(DetailAST ast) { 167 // don't flag interfaces 168 final DetailAST container = ast.getParent().getParent(); 169 if (container.getType() != TokenTypes.INTERFACE_DEF) { 170 if (ast.getType() == TokenTypes.LITERAL_CATCH) { 171 visitCatch(ast); 172 } 173 else if (ast.getType() == TokenTypes.FOR_EACH_CLAUSE) { 174 visitForEachClause(ast); 175 } 176 else { 177 visitMethod(ast); 178 } 179 } 180 } 181 182 /** 183 * Checks parameters of the method or ctor. 184 * 185 * @param method method or ctor to check. 186 */ 187 private void visitMethod(final DetailAST method) { 188 final DetailAST modifiers = 189 method.findFirstToken(TokenTypes.MODIFIERS); 190 191 // ignore abstract and native methods 192 if (modifiers.findFirstToken(TokenTypes.ABSTRACT) == null 193 && modifiers.findFirstToken(TokenTypes.LITERAL_NATIVE) == null) { 194 final DetailAST parameters = 195 method.findFirstToken(TokenTypes.PARAMETERS); 196 TokenUtil.forEachChild(parameters, TokenTypes.PARAMETER_DEF, this::checkParam); 197 } 198 } 199 200 /** 201 * Checks parameter of the catch block. 202 * 203 * @param catchClause catch block to check. 204 */ 205 private void visitCatch(final DetailAST catchClause) { 206 checkParam(catchClause.findFirstToken(TokenTypes.PARAMETER_DEF)); 207 } 208 209 /** 210 * Checks parameter of the for each clause. 211 * 212 * @param forEachClause for each clause to check. 213 */ 214 private void visitForEachClause(final DetailAST forEachClause) { 215 checkParam(forEachClause.findFirstToken(TokenTypes.VARIABLE_DEF)); 216 } 217 218 /** 219 * Checks if the given parameter is final. 220 * 221 * @param param parameter to check. 222 */ 223 private void checkParam(final DetailAST param) { 224 if (param.findFirstToken(TokenTypes.MODIFIERS).findFirstToken(TokenTypes.FINAL) == null 225 && !isIgnoredParam(param) 226 && !CheckUtil.isReceiverParameter(param)) { 227 final DetailAST paramName = param.findFirstToken(TokenTypes.IDENT); 228 final DetailAST firstNode = CheckUtil.getFirstNode(param); 229 log(firstNode, 230 MSG_KEY, paramName.getText()); 231 } 232 } 233 234 /** 235 * Checks for skip current param due to <b>ignorePrimitiveTypes</b> option. 236 * 237 * @param paramDef {@link TokenTypes#PARAMETER_DEF PARAMETER_DEF} 238 * @return true if param has to be skipped. 239 */ 240 private boolean isIgnoredParam(DetailAST paramDef) { 241 boolean result = false; 242 if (ignorePrimitiveTypes) { 243 final DetailAST parameterType = paramDef 244 .findFirstToken(TokenTypes.TYPE).getFirstChild(); 245 if (primitiveDataTypes.contains(parameterType.getType())) { 246 result = true; 247 } 248 } 249 return result; 250 } 251 252}