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