1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks;
21
22 import java.util.BitSet;
23
24 import com.puppycrawl.tools.checkstyle.StatelessCheck;
25 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
26 import com.puppycrawl.tools.checkstyle.api.DetailAST;
27 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
28 import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
29 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
30 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 @StatelessCheck
49 public class FinalParametersCheck extends AbstractCheck {
50
51
52
53
54
55 public static final String MSG_KEY = "final.parameter";
56
57
58
59
60
61
62 private final BitSet primitiveDataTypes = TokenUtil.asBitSet(
63 TokenTypes.LITERAL_BYTE,
64 TokenTypes.LITERAL_SHORT,
65 TokenTypes.LITERAL_INT,
66 TokenTypes.LITERAL_LONG,
67 TokenTypes.LITERAL_FLOAT,
68 TokenTypes.LITERAL_DOUBLE,
69 TokenTypes.LITERAL_BOOLEAN,
70 TokenTypes.LITERAL_CHAR
71 );
72
73
74
75
76 private boolean ignorePrimitiveTypes;
77
78
79
80
81
82 private boolean ignoreUnnamedParameters = true;
83
84
85
86
87
88
89
90 public void setIgnorePrimitiveTypes(boolean ignorePrimitiveTypes) {
91 this.ignorePrimitiveTypes = ignorePrimitiveTypes;
92 }
93
94
95
96
97
98
99
100
101
102 public void setIgnoreUnnamedParameters(boolean ignoreUnnamedParameters) {
103 this.ignoreUnnamedParameters = ignoreUnnamedParameters;
104 }
105
106 @Override
107 public int[] getDefaultTokens() {
108 return new int[] {
109 TokenTypes.METHOD_DEF,
110 TokenTypes.CTOR_DEF,
111 };
112 }
113
114 @Override
115 public int[] getAcceptableTokens() {
116 return new int[] {
117 TokenTypes.METHOD_DEF,
118 TokenTypes.CTOR_DEF,
119 TokenTypes.LITERAL_CATCH,
120 TokenTypes.FOR_EACH_CLAUSE,
121 TokenTypes.PATTERN_VARIABLE_DEF,
122 };
123 }
124
125 @Override
126 public int[] getRequiredTokens() {
127 return CommonUtil.EMPTY_INT_ARRAY;
128 }
129
130 @Override
131 public void visitToken(DetailAST ast) {
132 if (ast.getType() == TokenTypes.LITERAL_CATCH) {
133 visitCatch(ast);
134 }
135 else if (ast.getType() == TokenTypes.FOR_EACH_CLAUSE) {
136 visitForEachClause(ast);
137 }
138 else if (ast.getType() == TokenTypes.PATTERN_VARIABLE_DEF) {
139 visitPatternVariableDef(ast);
140 }
141 else {
142 visitMethod(ast);
143 }
144 }
145
146
147
148
149
150
151 private void visitPatternVariableDef(final DetailAST patternVariableDef) {
152 checkParam(patternVariableDef);
153 }
154
155
156
157
158
159
160 private void visitMethod(final DetailAST method) {
161
162
163
164
165 if (method.findFirstToken(TokenTypes.SLIST) != null) {
166 final DetailAST parameters =
167 method.findFirstToken(TokenTypes.PARAMETERS);
168 TokenUtil.forEachChild(parameters, TokenTypes.PARAMETER_DEF, this::checkParam);
169 }
170 }
171
172
173
174
175
176
177 private void visitCatch(final DetailAST catchClause) {
178 checkParam(catchClause.findFirstToken(TokenTypes.PARAMETER_DEF));
179 }
180
181
182
183
184
185
186 private void visitForEachClause(final DetailAST forEachClause) {
187 final DetailAST variableDef = forEachClause.findFirstToken(TokenTypes.VARIABLE_DEF);
188 if (variableDef != null) {
189
190
191 checkParam(variableDef);
192 }
193 }
194
195
196
197
198
199
200 private void checkParam(final DetailAST param) {
201 if (param.findFirstToken(TokenTypes.MODIFIERS).findFirstToken(TokenTypes.FINAL) == null
202 && !isIgnoredPrimitiveParam(param)
203 && !isIgnoredUnnamedParam(param)
204 && !CheckUtil.isReceiverParameter(param)) {
205 final DetailAST paramName = param.findFirstToken(TokenTypes.IDENT);
206 final DetailAST firstNode = CheckUtil.getFirstNode(param);
207 log(firstNode,
208 MSG_KEY, paramName.getText());
209 }
210 }
211
212
213
214
215
216
217
218 private boolean isIgnoredPrimitiveParam(DetailAST paramDef) {
219 boolean result = false;
220 if (ignorePrimitiveTypes) {
221 final DetailAST type = paramDef.findFirstToken(TokenTypes.TYPE);
222 final DetailAST parameterType = type.getFirstChild();
223 final DetailAST arrayDeclarator = type
224 .findFirstToken(TokenTypes.ARRAY_DECLARATOR);
225 if (arrayDeclarator == null
226 && primitiveDataTypes.get(parameterType.getType())) {
227 result = true;
228 }
229 }
230 return result;
231 }
232
233
234
235
236
237
238
239 private boolean isIgnoredUnnamedParam(final DetailAST paramDef) {
240 final DetailAST paramName = paramDef.findFirstToken(TokenTypes.IDENT);
241 return ignoreUnnamedParameters && paramName != null && "_".equals(paramName.getText());
242 }
243
244 }