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.coding;
21
22 import java.util.HashSet;
23 import java.util.Locale;
24 import java.util.Objects;
25 import java.util.Set;
26 import java.util.regex.Pattern;
27
28 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
29 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
30 import com.puppycrawl.tools.checkstyle.api.DetailAST;
31 import com.puppycrawl.tools.checkstyle.api.Scope;
32 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
33 import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
34 import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 @FileStatefulCheck
132 public class HiddenFieldCheck
133 extends AbstractCheck {
134
135
136
137
138
139 public static final String MSG_KEY = "hidden.field";
140
141
142
143
144 private FieldFrame frame;
145
146
147 private Pattern ignoreFormat;
148
149
150 private boolean ignoreSetter;
151
152
153
154
155
156
157
158 private boolean setterCanReturnItsClass;
159
160
161 private boolean ignoreConstructorParameter;
162
163
164 private boolean ignoreAbstractMethods;
165
166 @Override
167 public int[] getDefaultTokens() {
168 return getAcceptableTokens();
169 }
170
171 @Override
172 public int[] getAcceptableTokens() {
173 return new int[] {
174 TokenTypes.VARIABLE_DEF,
175 TokenTypes.PARAMETER_DEF,
176 TokenTypes.CLASS_DEF,
177 TokenTypes.ENUM_DEF,
178 TokenTypes.ENUM_CONSTANT_DEF,
179 TokenTypes.LAMBDA,
180 };
181 }
182
183 @Override
184 public int[] getRequiredTokens() {
185 return new int[] {
186 TokenTypes.CLASS_DEF,
187 TokenTypes.ENUM_DEF,
188 TokenTypes.ENUM_CONSTANT_DEF,
189 };
190 }
191
192 @Override
193 public void beginTree(DetailAST rootAST) {
194 frame = new FieldFrame(null, true, null);
195 }
196
197 @Override
198 public void visitToken(DetailAST ast) {
199 final int type = ast.getType();
200 switch (type) {
201 case TokenTypes.VARIABLE_DEF:
202 case TokenTypes.PARAMETER_DEF:
203 processVariable(ast);
204 break;
205 case TokenTypes.LAMBDA:
206 processLambda(ast);
207 break;
208 default:
209 visitOtherTokens(ast, type);
210 }
211 }
212
213
214
215
216
217
218
219
220 private void processLambda(DetailAST ast) {
221 final DetailAST firstChild = ast.getFirstChild();
222 if (firstChild.getType() == TokenTypes.IDENT) {
223 final String untypedLambdaParameterName = firstChild.getText();
224 if (frame.containsStaticField(untypedLambdaParameterName)
225 || isInstanceField(firstChild, untypedLambdaParameterName)) {
226 log(firstChild, MSG_KEY, untypedLambdaParameterName);
227 }
228 }
229 else {
230
231 processVariable(ast);
232 }
233 }
234
235
236
237
238
239
240
241
242 private void visitOtherTokens(DetailAST ast, int type) {
243
244
245
246
247
248 final DetailAST typeMods = ast.findFirstToken(TokenTypes.MODIFIERS);
249 final boolean isStaticInnerType =
250 typeMods != null
251 && typeMods.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
252 final String frameName;
253
254 if (type == TokenTypes.CLASS_DEF || type == TokenTypes.ENUM_DEF) {
255 frameName = ast.findFirstToken(TokenTypes.IDENT).getText();
256 }
257 else {
258 frameName = null;
259 }
260 final FieldFrame newFrame = new FieldFrame(frame, isStaticInnerType, frameName);
261
262
263 final DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK);
264
265 if (objBlock != null) {
266 DetailAST child = objBlock.getFirstChild();
267 while (child != null) {
268 if (child.getType() == TokenTypes.VARIABLE_DEF) {
269 final String name =
270 child.findFirstToken(TokenTypes.IDENT).getText();
271 final DetailAST mods =
272 child.findFirstToken(TokenTypes.MODIFIERS);
273 if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
274 newFrame.addInstanceField(name);
275 }
276 else {
277 newFrame.addStaticField(name);
278 }
279 }
280 child = child.getNextSibling();
281 }
282 }
283
284 frame = newFrame;
285 }
286
287 @Override
288 public void leaveToken(DetailAST ast) {
289 if (ast.getType() == TokenTypes.CLASS_DEF
290 || ast.getType() == TokenTypes.ENUM_DEF
291 || ast.getType() == TokenTypes.ENUM_CONSTANT_DEF) {
292
293 frame = frame.getParent();
294 }
295 }
296
297
298
299
300
301
302
303 private void processVariable(DetailAST ast) {
304 if (!ScopeUtil.isInInterfaceOrAnnotationBlock(ast)
305 && !CheckUtil.isReceiverParameter(ast)
306 && (ScopeUtil.isLocalVariableDef(ast)
307 || ast.getType() == TokenTypes.PARAMETER_DEF)) {
308
309 final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
310 final String name = nameAST.getText();
311
312 if ((frame.containsStaticField(name) || isInstanceField(ast, name))
313 && !isMatchingRegexp(name)
314 && !isIgnoredParam(ast, name)) {
315 log(nameAST, MSG_KEY, name);
316 }
317 }
318 }
319
320
321
322
323
324
325
326 private boolean isIgnoredParam(DetailAST ast, String name) {
327 return isIgnoredSetterParam(ast, name)
328 || isIgnoredConstructorParam(ast)
329 || isIgnoredParamOfAbstractMethod(ast);
330 }
331
332
333
334
335
336
337
338 private boolean isInstanceField(DetailAST ast, String name) {
339 return !isInStatic(ast) && frame.containsInstanceField(name);
340 }
341
342
343
344
345
346
347 private boolean isMatchingRegexp(String name) {
348 return ignoreFormat != null && ignoreFormat.matcher(name).find();
349 }
350
351
352
353
354
355
356
357 private static boolean isInStatic(DetailAST ast) {
358 DetailAST parent = ast.getParent();
359 boolean inStatic = false;
360
361 while (parent != null && !inStatic) {
362 if (parent.getType() == TokenTypes.STATIC_INIT) {
363 inStatic = true;
364 }
365 else if (parent.getType() == TokenTypes.METHOD_DEF
366 && !ScopeUtil.isInScope(parent, Scope.ANONINNER)
367 || parent.getType() == TokenTypes.VARIABLE_DEF) {
368 final DetailAST mods =
369 parent.findFirstToken(TokenTypes.MODIFIERS);
370 inStatic = mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
371 break;
372 }
373 else {
374 parent = parent.getParent();
375 }
376 }
377 return inStatic;
378 }
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394 private boolean isIgnoredSetterParam(DetailAST ast, String name) {
395 boolean isIgnoredSetterParam = false;
396 if (ignoreSetter && ast.getType() == TokenTypes.PARAMETER_DEF) {
397 final DetailAST parametersAST = ast.getParent();
398 final DetailAST methodAST = parametersAST.getParent();
399 if (parametersAST.getChildCount() == 1
400 && methodAST.getType() == TokenTypes.METHOD_DEF
401 && isSetterMethod(methodAST, name)) {
402 isIgnoredSetterParam = true;
403 }
404 }
405 return isIgnoredSetterParam;
406 }
407
408
409
410
411
412
413
414
415
416
417 private boolean isSetterMethod(DetailAST aMethodAST, String aName) {
418 final String methodName =
419 aMethodAST.findFirstToken(TokenTypes.IDENT).getText();
420 boolean isSetterMethod = false;
421
422 if (("set" + capitalize(aName)).equals(methodName)) {
423
424
425
426 final DetailAST typeAST = aMethodAST.findFirstToken(TokenTypes.TYPE);
427 final String returnType = typeAST.getFirstChild().getText();
428 if (typeAST.findFirstToken(TokenTypes.LITERAL_VOID) != null
429 || setterCanReturnItsClass && frame.isEmbeddedIn(returnType)) {
430
431
432
433
434
435
436
437
438
439
440
441 isSetterMethod = true;
442 }
443 }
444
445 return isSetterMethod;
446 }
447
448
449
450
451
452
453
454 private static String capitalize(final String name) {
455 String setterName = name;
456
457
458
459 if (name.length() == 1 || !Character.isUpperCase(name.charAt(1))) {
460 setterName = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
461 }
462 return setterName;
463 }
464
465
466
467
468
469
470
471
472 private boolean isIgnoredConstructorParam(DetailAST ast) {
473 boolean result = false;
474 if (ignoreConstructorParameter
475 && ast.getType() == TokenTypes.PARAMETER_DEF) {
476 final DetailAST parametersAST = ast.getParent();
477 final DetailAST constructorAST = parametersAST.getParent();
478 result = constructorAST.getType() == TokenTypes.CTOR_DEF;
479 }
480 return result;
481 }
482
483
484
485
486
487
488
489
490 private boolean isIgnoredParamOfAbstractMethod(DetailAST ast) {
491 boolean result = false;
492 if (ignoreAbstractMethods
493 && ast.getType() == TokenTypes.PARAMETER_DEF) {
494 final DetailAST method = ast.getParent().getParent();
495 if (method.getType() == TokenTypes.METHOD_DEF) {
496 final DetailAST mods = method.findFirstToken(TokenTypes.MODIFIERS);
497 result = mods.findFirstToken(TokenTypes.ABSTRACT) != null;
498 }
499 }
500 return result;
501 }
502
503
504
505
506
507 public void setIgnoreFormat(Pattern pattern) {
508 ignoreFormat = pattern;
509 }
510
511
512
513
514
515
516 public void setIgnoreSetter(boolean ignoreSetter) {
517 this.ignoreSetter = ignoreSetter;
518 }
519
520
521
522
523
524
525
526
527
528
529
530 public void setSetterCanReturnItsClass(
531 boolean aSetterCanReturnItsClass) {
532 setterCanReturnItsClass = aSetterCanReturnItsClass;
533 }
534
535
536
537
538
539
540 public void setIgnoreConstructorParameter(
541 boolean ignoreConstructorParameter) {
542 this.ignoreConstructorParameter = ignoreConstructorParameter;
543 }
544
545
546
547
548
549
550 public void setIgnoreAbstractMethods(
551 boolean ignoreAbstractMethods) {
552 this.ignoreAbstractMethods = ignoreAbstractMethods;
553 }
554
555
556
557
558 private static class FieldFrame {
559
560
561 private final String frameName;
562
563
564 private final boolean staticType;
565
566
567 private final FieldFrame parent;
568
569
570 private final Set<String> instanceFields = new HashSet<>();
571
572
573 private final Set<String> staticFields = new HashSet<>();
574
575
576
577
578
579
580
581 FieldFrame(FieldFrame parent, boolean staticType, String frameName) {
582 this.parent = parent;
583 this.staticType = staticType;
584 this.frameName = frameName;
585 }
586
587
588
589
590
591 public void addInstanceField(String field) {
592 instanceFields.add(field);
593 }
594
595
596
597
598
599 public void addStaticField(String field) {
600 staticFields.add(field);
601 }
602
603
604
605
606
607
608 public boolean containsInstanceField(String field) {
609 return instanceFields.contains(field)
610 || parent != null
611 && !staticType
612 && parent.containsInstanceField(field);
613 }
614
615
616
617
618
619
620 public boolean containsStaticField(String field) {
621 return staticFields.contains(field)
622 || parent != null
623 && parent.containsStaticField(field);
624 }
625
626
627
628
629
630 public FieldFrame getParent() {
631 return parent;
632 }
633
634
635
636
637
638
639
640
641
642
643
644 private boolean isEmbeddedIn(String classOrEnumName) {
645 FieldFrame currentFrame = this;
646 boolean isEmbeddedIn = false;
647 while (currentFrame != null) {
648 if (Objects.equals(currentFrame.frameName, classOrEnumName)) {
649 isEmbeddedIn = true;
650 break;
651 }
652 currentFrame = currentFrame.parent;
653 }
654 return isEmbeddedIn;
655 }
656
657 }
658
659 }