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 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
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
132
133
134
135
136
137
138
139
140
141
142
143
144 @FileStatefulCheck
145 public class HiddenFieldCheck
146 extends AbstractCheck {
147
148
149
150
151
152 public static final String MSG_KEY = "hidden.field";
153
154
155
156
157
158 private FieldFrame frame;
159
160
161 private Pattern ignoreFormat;
162
163
164
165
166 private boolean ignoreSetter;
167
168
169
170
171
172 private boolean setterCanReturnItsClass;
173
174
175 private boolean ignoreConstructorParameter;
176
177
178 private boolean ignoreAbstractMethods;
179
180 @Override
181 public int[] getDefaultTokens() {
182 return getAcceptableTokens();
183 }
184
185 @Override
186 public int[] getAcceptableTokens() {
187 return new int[] {
188 TokenTypes.VARIABLE_DEF,
189 TokenTypes.PARAMETER_DEF,
190 TokenTypes.CLASS_DEF,
191 TokenTypes.ENUM_DEF,
192 TokenTypes.ENUM_CONSTANT_DEF,
193 TokenTypes.PATTERN_VARIABLE_DEF,
194 TokenTypes.LAMBDA,
195 TokenTypes.RECORD_DEF,
196 TokenTypes.RECORD_COMPONENT_DEF,
197 };
198 }
199
200 @Override
201 public int[] getRequiredTokens() {
202 return new int[] {
203 TokenTypes.CLASS_DEF,
204 TokenTypes.ENUM_DEF,
205 TokenTypes.ENUM_CONSTANT_DEF,
206 TokenTypes.RECORD_DEF,
207 };
208 }
209
210 @Override
211 public void beginTree(DetailAST rootAST) {
212 frame = new FieldFrame(null, true, null);
213 }
214
215 @Override
216 public void visitToken(DetailAST ast) {
217 final int type = ast.getType();
218 switch (type) {
219 case TokenTypes.VARIABLE_DEF:
220 case TokenTypes.PARAMETER_DEF:
221 case TokenTypes.PATTERN_VARIABLE_DEF:
222 case TokenTypes.RECORD_COMPONENT_DEF:
223 processVariable(ast);
224 break;
225 case TokenTypes.LAMBDA:
226 processLambda(ast);
227 break;
228 default:
229 visitOtherTokens(ast, type);
230 }
231 }
232
233
234
235
236
237
238
239
240
241 private void processLambda(DetailAST ast) {
242 final DetailAST firstChild = ast.getFirstChild();
243 if (TokenUtil.isOfType(firstChild, TokenTypes.IDENT)) {
244 final String untypedLambdaParameterName = firstChild.getText();
245 if (frame.containsStaticField(untypedLambdaParameterName)
246 || isInstanceField(firstChild, untypedLambdaParameterName)) {
247 log(firstChild, MSG_KEY, untypedLambdaParameterName);
248 }
249 }
250 }
251
252
253
254
255
256
257
258
259 private void visitOtherTokens(DetailAST ast, int type) {
260
261
262
263
264
265 final DetailAST typeMods = ast.findFirstToken(TokenTypes.MODIFIERS);
266 final boolean isStaticInnerType =
267 typeMods != null
268 && typeMods.findFirstToken(TokenTypes.LITERAL_STATIC) != null
269
270 || ast.getType() == TokenTypes.RECORD_DEF;
271 final String frameName;
272
273 if (type == TokenTypes.CLASS_DEF
274 || type == TokenTypes.ENUM_DEF) {
275 frameName = ast.findFirstToken(TokenTypes.IDENT).getText();
276 }
277 else {
278 frameName = null;
279 }
280 final FieldFrame newFrame = new FieldFrame(frame, isStaticInnerType, frameName);
281
282
283 final DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK);
284
285 if (objBlock != null) {
286 DetailAST child = objBlock.getFirstChild();
287 while (child != null) {
288 if (child.getType() == TokenTypes.VARIABLE_DEF) {
289 final String name =
290 child.findFirstToken(TokenTypes.IDENT).getText();
291 final DetailAST mods =
292 child.findFirstToken(TokenTypes.MODIFIERS);
293 if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
294 newFrame.addInstanceField(name);
295 }
296 else {
297 newFrame.addStaticField(name);
298 }
299 }
300 child = child.getNextSibling();
301 }
302 }
303 if (ast.getType() == TokenTypes.RECORD_DEF) {
304 final DetailAST recordComponents =
305 ast.findFirstToken(TokenTypes.RECORD_COMPONENTS);
306
307
308 TokenUtil.forEachChild(recordComponents,
309 TokenTypes.RECORD_COMPONENT_DEF, node -> {
310 final String name = node.findFirstToken(TokenTypes.IDENT).getText();
311 newFrame.addInstanceField(name);
312 });
313 }
314
315 frame = newFrame;
316 }
317
318 @Override
319 public void leaveToken(DetailAST ast) {
320 if (ast.getType() == TokenTypes.CLASS_DEF
321 || ast.getType() == TokenTypes.ENUM_DEF
322 || ast.getType() == TokenTypes.ENUM_CONSTANT_DEF
323 || ast.getType() == TokenTypes.RECORD_DEF) {
324
325 frame = frame.getParent();
326 }
327 }
328
329
330
331
332
333
334
335
336 private void processVariable(DetailAST ast) {
337 if (!ScopeUtil.isInInterfaceOrAnnotationBlock(ast)
338 && !CheckUtil.isReceiverParameter(ast)
339 && (ScopeUtil.isLocalVariableDef(ast)
340 || ast.getType() == TokenTypes.PARAMETER_DEF
341 || ast.getType() == TokenTypes.PATTERN_VARIABLE_DEF)) {
342
343 final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
344 final String name = nameAST.getText();
345
346 if ((frame.containsStaticField(name) || isInstanceField(ast, name))
347 && !isMatchingRegexp(name)
348 && !isIgnoredParam(ast, name)) {
349 log(nameAST, MSG_KEY, name);
350 }
351 }
352 }
353
354
355
356
357
358
359
360
361 private boolean isIgnoredParam(DetailAST ast, String name) {
362 return isIgnoredSetterParam(ast, name)
363 || isIgnoredConstructorParam(ast)
364 || isIgnoredParamOfAbstractMethod(ast);
365 }
366
367
368
369
370
371
372
373
374 private boolean isInstanceField(DetailAST ast, String name) {
375 return !isInStatic(ast) && frame.containsInstanceField(name);
376 }
377
378
379
380
381
382
383
384 private boolean isMatchingRegexp(String name) {
385 return ignoreFormat != null && ignoreFormat.matcher(name).find();
386 }
387
388
389
390
391
392
393
394
395 private static boolean isInStatic(DetailAST ast) {
396 DetailAST parent = ast.getParent();
397 boolean inStatic = false;
398
399 while (parent != null && !inStatic) {
400 if (parent.getType() == TokenTypes.STATIC_INIT) {
401 inStatic = true;
402 }
403 else if (parent.getType() == TokenTypes.METHOD_DEF
404 && !ScopeUtil.isInScope(parent, Scope.ANONINNER)
405 || parent.getType() == TokenTypes.VARIABLE_DEF) {
406 final DetailAST mods =
407 parent.findFirstToken(TokenTypes.MODIFIERS);
408 inStatic = mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
409 break;
410 }
411 else {
412 parent = parent.getParent();
413 }
414 }
415 return inStatic;
416 }
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432 private boolean isIgnoredSetterParam(DetailAST ast, String name) {
433 boolean isIgnoredSetterParam = false;
434 if (ignoreSetter) {
435 final DetailAST parametersAST = ast.getParent();
436 final DetailAST methodAST = parametersAST.getParent();
437 if (parametersAST.getChildCount() == 1
438 && methodAST.getType() == TokenTypes.METHOD_DEF
439 && isSetterMethod(methodAST, name)) {
440 isIgnoredSetterParam = true;
441 }
442 }
443 return isIgnoredSetterParam;
444 }
445
446
447
448
449
450
451
452
453
454
455 private boolean isSetterMethod(DetailAST aMethodAST, String aName) {
456 final String methodName =
457 aMethodAST.findFirstToken(TokenTypes.IDENT).getText();
458 boolean isSetterMethod = false;
459
460 if (("set" + capitalize(aName)).equals(methodName)) {
461
462
463
464 final DetailAST typeAST = aMethodAST.findFirstToken(TokenTypes.TYPE);
465 final String returnType = typeAST.getFirstChild().getText();
466 if (typeAST.findFirstToken(TokenTypes.LITERAL_VOID) != null
467 || setterCanReturnItsClass && frame.isEmbeddedIn(returnType)) {
468
469
470
471
472
473
474
475
476
477
478
479 isSetterMethod = true;
480 }
481 }
482
483 return isSetterMethod;
484 }
485
486
487
488
489
490
491
492
493 private static String capitalize(final String name) {
494 String setterName = name;
495
496
497
498 if (name.length() == 1 || !Character.isUpperCase(name.charAt(1))) {
499 setterName = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
500 }
501 return setterName;
502 }
503
504
505
506
507
508
509
510
511
512 private boolean isIgnoredConstructorParam(DetailAST ast) {
513 boolean result = false;
514 if (ignoreConstructorParameter
515 && ast.getType() == TokenTypes.PARAMETER_DEF) {
516 final DetailAST parametersAST = ast.getParent();
517 final DetailAST constructorAST = parametersAST.getParent();
518 result = constructorAST.getType() == TokenTypes.CTOR_DEF;
519 }
520 return result;
521 }
522
523
524
525
526
527
528
529
530
531 private boolean isIgnoredParamOfAbstractMethod(DetailAST ast) {
532 boolean result = false;
533 if (ignoreAbstractMethods) {
534 final DetailAST method = ast.getParent().getParent();
535 if (method.getType() == TokenTypes.METHOD_DEF) {
536 final DetailAST mods = method.findFirstToken(TokenTypes.MODIFIERS);
537 result = mods.findFirstToken(TokenTypes.ABSTRACT) != null;
538 }
539 }
540 return result;
541 }
542
543
544
545
546
547
548
549 public void setIgnoreFormat(Pattern pattern) {
550 ignoreFormat = pattern;
551 }
552
553
554
555
556
557
558
559
560 public void setIgnoreSetter(boolean ignoreSetter) {
561 this.ignoreSetter = ignoreSetter;
562 }
563
564
565
566
567
568
569
570
571
572
573
574
575 public void setSetterCanReturnItsClass(
576 boolean aSetterCanReturnItsClass) {
577 setterCanReturnItsClass = aSetterCanReturnItsClass;
578 }
579
580
581
582
583
584
585
586
587 public void setIgnoreConstructorParameter(
588 boolean ignoreConstructorParameter) {
589 this.ignoreConstructorParameter = ignoreConstructorParameter;
590 }
591
592
593
594
595
596
597
598
599 public void setIgnoreAbstractMethods(
600 boolean ignoreAbstractMethods) {
601 this.ignoreAbstractMethods = ignoreAbstractMethods;
602 }
603
604
605
606
607 private static final class FieldFrame {
608
609
610 private final String frameName;
611
612
613 private final boolean staticType;
614
615
616 private final FieldFrame parent;
617
618
619 private final Set<String> instanceFields = new HashSet<>();
620
621
622 private final Set<String> staticFields = new HashSet<>();
623
624
625
626
627
628
629
630
631 private FieldFrame(FieldFrame parent, boolean staticType, String frameName) {
632 this.parent = parent;
633 this.staticType = staticType;
634 this.frameName = frameName;
635 }
636
637
638
639
640
641
642 public void addInstanceField(String field) {
643 instanceFields.add(field);
644 }
645
646
647
648
649
650
651 public void addStaticField(String field) {
652 staticFields.add(field);
653 }
654
655
656
657
658
659
660
661 public boolean containsInstanceField(String field) {
662 FieldFrame currentParent = parent;
663 boolean contains = instanceFields.contains(field);
664 boolean isStaticType = staticType;
665 while (!isStaticType && !contains) {
666 contains = currentParent.instanceFields.contains(field);
667 isStaticType = currentParent.staticType;
668 currentParent = currentParent.parent;
669 }
670 return contains;
671 }
672
673
674
675
676
677
678
679 public boolean containsStaticField(String field) {
680 FieldFrame currentParent = parent;
681 boolean contains = staticFields.contains(field);
682 while (currentParent != null && !contains) {
683 contains = currentParent.staticFields.contains(field);
684 currentParent = currentParent.parent;
685 }
686 return contains;
687 }
688
689
690
691
692
693
694 public FieldFrame getParent() {
695 return parent;
696 }
697
698
699
700
701
702
703
704
705
706
707
708 private boolean isEmbeddedIn(String classOrEnumName) {
709 FieldFrame currentFrame = this;
710 boolean isEmbeddedIn = false;
711 while (currentFrame != null) {
712 if (Objects.equals(currentFrame.frameName, classOrEnumName)) {
713 isEmbeddedIn = true;
714 break;
715 }
716 currentFrame = currentFrame.parent;
717 }
718 return isEmbeddedIn;
719 }
720
721 }
722
723 }