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.ArrayDeque;
23 import java.util.BitSet;
24 import java.util.Deque;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Map;
28 import java.util.Queue;
29 import java.util.Set;
30
31 import javax.annotation.Nullable;
32
33 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
34 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
35 import com.puppycrawl.tools.checkstyle.api.DetailAST;
36 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
37 import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
38 import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
39 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
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 @FileStatefulCheck
77 public class RequireThisCheck extends AbstractCheck {
78
79
80
81
82
83 public static final String MSG_METHOD = "require.this.method";
84
85
86
87
88 public static final String MSG_VARIABLE = "require.this.variable";
89
90
91 private static final BitSet DECLARATION_TOKENS = TokenUtil.asBitSet(
92 TokenTypes.VARIABLE_DEF,
93 TokenTypes.CTOR_DEF,
94 TokenTypes.METHOD_DEF,
95 TokenTypes.CLASS_DEF,
96 TokenTypes.ENUM_DEF,
97 TokenTypes.ANNOTATION_DEF,
98 TokenTypes.INTERFACE_DEF,
99 TokenTypes.PARAMETER_DEF,
100 TokenTypes.TYPE_ARGUMENT,
101 TokenTypes.RECORD_DEF,
102 TokenTypes.RECORD_COMPONENT_DEF,
103 TokenTypes.RESOURCE
104 );
105
106 private static final BitSet ASSIGN_TOKENS = TokenUtil.asBitSet(
107 TokenTypes.ASSIGN,
108 TokenTypes.PLUS_ASSIGN,
109 TokenTypes.STAR_ASSIGN,
110 TokenTypes.DIV_ASSIGN,
111 TokenTypes.MOD_ASSIGN,
112 TokenTypes.SR_ASSIGN,
113 TokenTypes.BSR_ASSIGN,
114 TokenTypes.SL_ASSIGN,
115 TokenTypes.BAND_ASSIGN,
116 TokenTypes.BXOR_ASSIGN
117 );
118
119 private static final BitSet COMPOUND_ASSIGN_TOKENS = TokenUtil.asBitSet(
120 TokenTypes.PLUS_ASSIGN,
121 TokenTypes.STAR_ASSIGN,
122 TokenTypes.DIV_ASSIGN,
123 TokenTypes.MOD_ASSIGN,
124 TokenTypes.SR_ASSIGN,
125 TokenTypes.BSR_ASSIGN,
126 TokenTypes.SL_ASSIGN,
127 TokenTypes.BAND_ASSIGN,
128 TokenTypes.BXOR_ASSIGN
129 );
130
131
132 private final Deque<AbstractFrame> current = new ArrayDeque<>();
133
134
135 private Map<DetailAST, AbstractFrame> frames;
136
137
138 private boolean checkFields = true;
139
140 private boolean checkMethods = true;
141
142 private boolean validateOnlyOverlapping = true;
143
144
145
146
147 public RequireThisCheck() {
148
149 }
150
151
152
153
154
155
156
157 public void setCheckFields(boolean checkFields) {
158 this.checkFields = checkFields;
159 }
160
161
162
163
164
165
166
167 public void setCheckMethods(boolean checkMethods) {
168 this.checkMethods = checkMethods;
169 }
170
171
172
173
174
175
176
177 public void setValidateOnlyOverlapping(boolean validateOnlyOverlapping) {
178 this.validateOnlyOverlapping = validateOnlyOverlapping;
179 }
180
181 @Override
182 public int[] getDefaultTokens() {
183 return getRequiredTokens();
184 }
185
186 @Override
187 public int[] getRequiredTokens() {
188 return new int[] {
189 TokenTypes.CLASS_DEF,
190 TokenTypes.INTERFACE_DEF,
191 TokenTypes.ENUM_DEF,
192 TokenTypes.ANNOTATION_DEF,
193 TokenTypes.CTOR_DEF,
194 TokenTypes.METHOD_DEF,
195 TokenTypes.LITERAL_FOR,
196 TokenTypes.SLIST,
197 TokenTypes.IDENT,
198 TokenTypes.RECORD_DEF,
199 TokenTypes.COMPACT_CTOR_DEF,
200 TokenTypes.LITERAL_TRY,
201 TokenTypes.RESOURCE,
202 TokenTypes.COMPACT_COMPILATION_UNIT,
203 };
204 }
205
206 @Override
207 public int[] getAcceptableTokens() {
208 return getRequiredTokens();
209 }
210
211 @Override
212 public void beginTree(DetailAST rootAST) {
213 frames = new HashMap<>();
214 current.clear();
215
216 final Deque<AbstractFrame> frameStack = new ArrayDeque<>();
217 DetailAST curNode = rootAST;
218 while (curNode != null) {
219 collectDeclarations(frameStack, curNode);
220 DetailAST toVisit = curNode.getFirstChild();
221 while (curNode != null && toVisit == null) {
222 endCollectingDeclarations(frameStack, curNode);
223 toVisit = curNode.getNextSibling();
224 curNode = curNode.getParent();
225 }
226 curNode = toVisit;
227 }
228 }
229
230 @Override
231 public void visitToken(DetailAST ast) {
232 switch (ast.getType()) {
233 case TokenTypes.IDENT -> processIdent(ast);
234 case TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF, TokenTypes.ENUM_DEF,
235 TokenTypes.ANNOTATION_DEF, TokenTypes.SLIST, TokenTypes.METHOD_DEF,
236 TokenTypes.CTOR_DEF, TokenTypes.LITERAL_FOR, TokenTypes.RECORD_DEF,
237 TokenTypes.COMPACT_COMPILATION_UNIT ->
238 current.push(frames.get(ast));
239 case TokenTypes.LITERAL_TRY -> {
240 if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) {
241 current.push(frames.get(ast));
242 }
243 }
244 default -> {
245
246 }
247 }
248 }
249
250 @Override
251 public void leaveToken(DetailAST ast) {
252 switch (ast.getType()) {
253 case TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF, TokenTypes.ENUM_DEF,
254 TokenTypes.ANNOTATION_DEF, TokenTypes.SLIST, TokenTypes.METHOD_DEF,
255 TokenTypes.CTOR_DEF, TokenTypes.LITERAL_FOR,
256 TokenTypes.RECORD_DEF -> current.pop();
257 case TokenTypes.LITERAL_TRY -> {
258 if (current.peek().getType() == FrameType.TRY_WITH_RESOURCES_FRAME) {
259 current.pop();
260 }
261 }
262 default -> {
263
264 }
265 }
266 }
267
268
269
270
271
272
273
274 private void processIdent(DetailAST ast) {
275 if (!shouldSkipAnnotationContext(ast)) {
276 final int parentType = ast.getParent().getType();
277 if (parentType == TokenTypes.METHOD_CALL) {
278 if (checkMethods) {
279 final AbstractFrame frame = getMethodWithoutThis(ast);
280 if (frame != null) {
281 logViolation(MSG_METHOD, ast, frame);
282 }
283 }
284 }
285 else {
286 if (checkFields) {
287 final AbstractFrame frame = getFieldWithoutThis(ast, parentType);
288 final boolean canUseThis = !isInCompactConstructor(ast);
289 if (frame != null && canUseThis) {
290 logViolation(MSG_VARIABLE, ast, frame);
291 }
292 }
293 }
294 }
295 }
296
297
298
299
300
301
302
303
304
305
306
307
308 private static boolean shouldSkipAnnotationContext(DetailAST ast) {
309 return isInsideAnnotationFieldDef(ast) || isAnnotationStructuralElement(ast);
310 }
311
312
313
314
315
316
317
318
319 private static boolean isInsideAnnotationFieldDef(DetailAST ast) {
320 DetailAST current = ast;
321 boolean insideAnnotationFieldDef = false;
322 while (current != null) {
323 if (current.getType() == TokenTypes.ANNOTATION_FIELD_DEF) {
324 insideAnnotationFieldDef = true;
325 break;
326 }
327 current = current.getParent();
328 }
329 return insideAnnotationFieldDef;
330 }
331
332
333
334
335
336
337
338
339
340
341 private static boolean isAnnotationStructuralElement(DetailAST ast) {
342 DetailAST current = ast.getParent();
343 final int parentType = current.getType();
344 while (current.getType() == TokenTypes.DOT) {
345 current = current.getParent();
346 }
347 final int topType = current.getType();
348 return topType == TokenTypes.ANNOTATION
349 || parentType == TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR;
350 }
351
352
353
354
355
356
357
358
359 private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) {
360 if (frame.getFrameName().equals(getNearestClassFrameName())) {
361 log(ast, msgKey, ast.getText(), "");
362 }
363 else if (!(frame instanceof AnonymousClassFrame)
364 && !(frame instanceof CompactCompilationUnitFrame)) {
365 log(ast, msgKey, ast.getText(), frame.getFrameName() + '.');
366 }
367 }
368
369
370
371
372
373
374
375
376
377
378 private AbstractFrame getFieldWithoutThis(DetailAST ast, int parentType) {
379 final boolean importOrPackage = ScopeUtil.getSurroundingScope(ast).isEmpty();
380 final boolean typeName = parentType == TokenTypes.TYPE
381 || parentType == TokenTypes.LITERAL_NEW;
382 AbstractFrame frame = null;
383
384 if (!importOrPackage
385 && !typeName
386 && !DECLARATION_TOKENS.get(parentType)
387 && !isLambdaParameter(ast)) {
388 final AbstractFrame fieldFrame = findClassFrame(ast, LookMode.NO_LOOK_FOR_METHOD);
389
390 if (fieldFrame != null && ((ClassFrame) fieldFrame).hasInstanceMember(ast)) {
391 frame = getClassFrameWhereViolationIsFound(ast);
392 }
393 }
394 return frame;
395 }
396
397
398
399
400
401
402
403 private static boolean isInCompactConstructor(DetailAST ast) {
404 boolean isInCompactCtor = false;
405 DetailAST parent = ast;
406 while (parent != null) {
407 if (parent.getType() == TokenTypes.COMPACT_CTOR_DEF) {
408 isInCompactCtor = true;
409 break;
410 }
411 parent = parent.getParent();
412 }
413 return isInCompactCtor;
414 }
415
416
417
418
419
420
421
422
423 private static void collectDeclarations(Deque<AbstractFrame> frameStack, DetailAST ast) {
424 final AbstractFrame frame = frameStack.peek();
425
426 switch (ast.getType()) {
427 case TokenTypes.VARIABLE_DEF -> collectVariableDeclarations(ast, frame);
428
429 case TokenTypes.RECORD_COMPONENT_DEF -> {
430 final DetailAST componentIdent = ast.findFirstToken(TokenTypes.IDENT);
431 ((ClassFrame) frame).addInstanceMember(componentIdent);
432 }
433
434 case TokenTypes.PARAMETER_DEF -> {
435 if (!CheckUtil.isReceiverParameter(ast) && !isLambdaParameter(ast)) {
436 final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT);
437 frame.addIdent(parameterIdent);
438 }
439 }
440
441 case TokenTypes.RESOURCE -> {
442 final DetailAST resourceIdent = ast.findFirstToken(TokenTypes.IDENT);
443 if (resourceIdent != null) {
444 frame.addIdent(resourceIdent);
445 }
446 }
447
448 case TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF, TokenTypes.ENUM_DEF,
449 TokenTypes.ANNOTATION_DEF, TokenTypes.RECORD_DEF -> {
450 final DetailAST classFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
451 frameStack.addFirst(new ClassFrame(frame, classFrameNameIdent));
452 }
453
454 case TokenTypes.COMPACT_COMPILATION_UNIT ->
455 frameStack.addFirst(new CompactCompilationUnitFrame(frame, ast));
456
457 case TokenTypes.SLIST -> frameStack.addFirst(new BlockFrame(frame, ast));
458
459 case TokenTypes.METHOD_DEF -> collectMethodDeclarations(frameStack, ast, frame);
460
461 case TokenTypes.CTOR_DEF, TokenTypes.COMPACT_CTOR_DEF -> {
462 final DetailAST ctorFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
463 frameStack.addFirst(new ConstructorFrame(frame, ctorFrameNameIdent));
464 }
465
466 case TokenTypes.ENUM_CONSTANT_DEF -> {
467 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
468 ((ClassFrame) frame).addStaticMember(ident);
469 }
470
471 case TokenTypes.LITERAL_CATCH -> {
472 final AbstractFrame catchFrame = new CatchFrame(frame, ast);
473 frameStack.addFirst(catchFrame);
474 }
475
476 case TokenTypes.LITERAL_FOR -> {
477 final AbstractFrame forFrame = new ForFrame(frame, ast);
478 frameStack.addFirst(forFrame);
479 }
480
481 case TokenTypes.LITERAL_NEW -> {
482 final DetailAST lastChild = ast.getLastChild();
483 if (lastChild != null && lastChild.getType() == TokenTypes.OBJBLOCK) {
484 frameStack.addFirst(new AnonymousClassFrame(frame, ast.toString()));
485 }
486 }
487
488 case TokenTypes.LITERAL_TRY -> {
489 if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) {
490 frameStack.addFirst(new TryWithResourcesFrame(frame, ast));
491 }
492 }
493
494 default -> {
495
496 }
497 }
498 }
499
500
501
502
503
504
505
506 private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) {
507 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
508 if (frame.getType() == FrameType.CLASS_FRAME) {
509 final DetailAST mods =
510 ast.findFirstToken(TokenTypes.MODIFIERS);
511 if (ScopeUtil.isInInterfaceBlock(ast)
512 || ScopeUtil.isInAnnotationBlock(ast)
513 || mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
514 ((ClassFrame) frame).addStaticMember(ident);
515 }
516 else {
517 ((ClassFrame) frame).addInstanceMember(ident);
518 }
519 }
520 else {
521 frame.addIdent(ident);
522 }
523 }
524
525
526
527
528
529
530
531
532 private static void collectMethodDeclarations(Deque<AbstractFrame> frameStack,
533 DetailAST ast, AbstractFrame frame) {
534 final DetailAST methodFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
535 final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
536 if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
537 ((ClassFrame) frame).addInstanceMethod(methodFrameNameIdent);
538 }
539 else {
540 ((ClassFrame) frame).addStaticMethod(methodFrameNameIdent);
541 }
542 frameStack.addFirst(new MethodFrame(frame, methodFrameNameIdent));
543 }
544
545
546
547
548
549
550
551 private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, DetailAST ast) {
552 switch (ast.getType()) {
553 case TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF, TokenTypes.ENUM_DEF,
554 TokenTypes.ANNOTATION_DEF, TokenTypes.SLIST, TokenTypes.METHOD_DEF,
555 TokenTypes.CTOR_DEF, TokenTypes.LITERAL_CATCH, TokenTypes.LITERAL_FOR,
556 TokenTypes.RECORD_DEF, TokenTypes.COMPACT_CTOR_DEF,
557 TokenTypes.COMPACT_COMPILATION_UNIT ->
558 frames.put(ast, frameStack.poll());
559
560 case TokenTypes.LITERAL_NEW -> {
561 final DetailAST lastChild = ast.getLastChild();
562 if (lastChild != null && lastChild.getType() == TokenTypes.OBJBLOCK) {
563 frameStack.remove();
564 }
565 }
566
567 case TokenTypes.LITERAL_TRY -> {
568 if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) {
569 frames.put(ast, frameStack.poll());
570 }
571 }
572
573 default -> {
574
575 }
576 }
577 }
578
579
580
581
582
583
584
585
586 @Nullable
587 private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) {
588 AbstractFrame frameWhereViolationIsFound = null;
589 final AbstractFrame variableDeclarationFrame = findFrame(ast, LookMode.NO_LOOK_FOR_METHOD);
590 final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType();
591
592 if (variableDeclarationFrameType == FrameType.CLASS_FRAME
593 && isViolationNoOverlapping(ast)) {
594 frameWhereViolationIsFound = variableDeclarationFrame;
595 }
596 else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) {
597 frameWhereViolationIsFound = getFrameForMethod(ast, variableDeclarationFrame);
598 }
599 else if (variableDeclarationFrameType == FrameType.CTOR_FRAME
600 && isOverlappingByArgument(ast)
601 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) {
602 frameWhereViolationIsFound = findFrame(ast, LookMode.LOOK_FOR_METHOD);
603 }
604 else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME
605 && isViolationForBlockFrame(ast, variableDeclarationFrame)) {
606 frameWhereViolationIsFound = findFrame(ast, LookMode.LOOK_FOR_METHOD);
607 }
608 return frameWhereViolationIsFound;
609 }
610
611
612
613
614
615
616
617 private boolean isViolationNoOverlapping(DetailAST ast) {
618 final DetailAST prevSibling = ast.getPreviousSibling();
619 final int parentType = ast.getParent().getType();
620 return !validateOnlyOverlapping
621 && (prevSibling == null
622 || parentType != TokenTypes.DOT && parentType != TokenTypes.METHOD_REF)
623 && canBeReferencedFromStaticContext(ast);
624 }
625
626
627
628
629
630
631
632
633 private boolean isViolationForBlockFrame(DetailAST ast,
634 AbstractFrame variableDeclarationFrame) {
635 return isOverlappingByLocalVariable(ast)
636 && canAssignValueToClassField(ast)
637 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
638 && !isReturnedVariable(variableDeclarationFrame, ast)
639 && canBeReferencedFromStaticContext(ast);
640 }
641
642
643
644
645
646
647
648
649
650 private AbstractFrame getFrameForMethod(DetailAST ast,
651 AbstractFrame variableDeclarationFrame) {
652 AbstractFrame frameWhereViolationIsFound = null;
653 if (isOverlappingByArgument(ast)) {
654 if (isViolationForMethodOverlapping(ast, variableDeclarationFrame)) {
655 frameWhereViolationIsFound = findFrame(ast, LookMode.LOOK_FOR_METHOD);
656 }
657 }
658 else if (isViolationForMethodNoOverlapping(ast, variableDeclarationFrame)) {
659 frameWhereViolationIsFound = findFrame(ast, LookMode.LOOK_FOR_METHOD);
660 }
661 return frameWhereViolationIsFound;
662 }
663
664
665
666
667
668
669
670
671 private boolean isViolationForMethodOverlapping(DetailAST ast,
672 AbstractFrame variableDeclarationFrame) {
673 return !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
674 && !isReturnedVariable(variableDeclarationFrame, ast)
675 && canBeReferencedFromStaticContext(ast)
676 && canAssignValueToClassField(ast);
677 }
678
679
680
681
682
683
684
685
686 private boolean isViolationForMethodNoOverlapping(DetailAST ast,
687 AbstractFrame variableDeclarationFrame) {
688 final DetailAST prevSibling = ast.getPreviousSibling();
689 return !validateOnlyOverlapping
690 && prevSibling == null
691 && ASSIGN_TOKENS.get(ast.getParent().getType())
692 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
693 && canBeReferencedFromStaticContext(ast)
694 && canAssignValueToClassField(ast);
695 }
696
697
698
699
700
701
702
703
704
705 private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame,
706 DetailAST ident) {
707 final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
708 final DetailAST definitionToken = blockFrameNameIdent.getParent();
709 final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
710 final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
711
712 boolean userDefinedArrangementOfThis = false;
713
714 final Set<DetailAST> variableUsagesInsideBlock =
715 getAllTokensWhichAreEqualToCurrent(definitionToken, ident,
716 blockEndToken.getLineNo());
717
718 for (DetailAST variableUsage : variableUsagesInsideBlock) {
719 final DetailAST prevSibling = variableUsage.getPreviousSibling();
720 if (prevSibling != null
721 && prevSibling.getType() == TokenTypes.LITERAL_THIS) {
722 userDefinedArrangementOfThis = true;
723 break;
724 }
725 }
726 return userDefinedArrangementOfThis;
727 }
728
729
730
731
732
733
734
735
736 private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) {
737 DetailAST blockEndToken = null;
738 final DetailAST blockNameIdentParent = blockNameIdent.getParent();
739 if (blockNameIdentParent.getType() == TokenTypes.CASE_GROUP) {
740 blockEndToken = blockNameIdentParent.getNextSibling();
741 }
742 else {
743 final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent,
744 TokenTypes.RCURLY);
745 for (DetailAST currentRcurly : rcurlyTokens) {
746 final DetailAST parent = currentRcurly.getParent();
747 if (TokenUtil.areOnSameLine(blockStartToken, parent)) {
748 blockEndToken = currentRcurly;
749 }
750 }
751 }
752 return blockEndToken;
753 }
754
755
756
757
758
759
760
761
762 private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) {
763 final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
764 final DetailAST definitionToken = blockFrameNameIdent.getParent();
765 final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
766 final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
767
768 final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken,
769 TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo());
770
771 return returnsInsideBlock.stream()
772 .anyMatch(returnToken -> isAstInside(returnToken, ident));
773 }
774
775
776
777
778
779
780
781
782 private static boolean isAstInside(DetailAST tree, DetailAST ast) {
783 boolean result = false;
784
785 if (isAstSimilar(tree, ast)) {
786 result = true;
787 }
788 else {
789 for (DetailAST child = tree.getFirstChild(); child != null
790 && !result; child = child.getNextSibling()) {
791 result = isAstInside(child, ast);
792 }
793 }
794
795 return result;
796 }
797
798
799
800
801
802
803
804 private static boolean canBeReferencedFromStaticContext(DetailAST ident) {
805 boolean staticContext = false;
806
807 final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident);
808 if (codeBlockDefinition != null) {
809 final DetailAST modifiers = codeBlockDefinition.getFirstChild();
810 staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT
811 || modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
812 }
813 return !staticContext;
814 }
815
816
817
818
819
820
821
822
823 private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) {
824 DetailAST parent = ident;
825 while (parent != null
826 && parent.getType() != TokenTypes.METHOD_DEF
827 && parent.getType() != TokenTypes.STATIC_INIT) {
828 parent = parent.getParent();
829 }
830 return parent;
831 }
832
833
834
835
836
837
838
839
840
841 private boolean canAssignValueToClassField(DetailAST ast) {
842 AbstractFrame fieldUsageFrame = findFrame(ast, LookMode.NO_LOOK_FOR_METHOD);
843 while (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
844 fieldUsageFrame = fieldUsageFrame.getParent();
845 }
846 final boolean fieldUsageInConstructor =
847 fieldUsageFrame.getType() == FrameType.CTOR_FRAME;
848
849 final AbstractFrame declarationFrame = findFrame(ast, LookMode.LOOK_FOR_METHOD);
850 final boolean finalField = ((ClassFrame) declarationFrame).hasFinalField(ast);
851
852 return fieldUsageInConstructor || !finalField;
853 }
854
855
856
857
858
859
860
861 private boolean isOverlappingByArgument(DetailAST ast) {
862 boolean overlapping = false;
863 final DetailAST parent = ast.getParent();
864 final DetailAST sibling = ast.getNextSibling();
865 if (sibling != null && ASSIGN_TOKENS.get(parent.getType())) {
866 if (COMPOUND_ASSIGN_TOKENS.get(parent.getType())) {
867 overlapping = true;
868 }
869 else {
870 final ClassFrame classFrame = (ClassFrame) findFrame(ast, LookMode.LOOK_FOR_METHOD);
871 final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
872 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
873 }
874 }
875 return overlapping;
876 }
877
878
879
880
881
882
883
884 private boolean isOverlappingByLocalVariable(DetailAST ast) {
885 boolean overlapping = false;
886 final DetailAST parent = ast.getParent();
887 if (ASSIGN_TOKENS.get(parent.getType())) {
888 final ClassFrame classFrame = (ClassFrame) findFrame(ast, LookMode.LOOK_FOR_METHOD);
889 final Set<DetailAST> exprIdents =
890 getAllTokensOfType(ast.getNextSibling(), TokenTypes.IDENT);
891 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
892 }
893 return overlapping;
894 }
895
896
897
898
899
900
901
902
903 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
904 DetailAST vertex = ast;
905 final Set<DetailAST> result = new HashSet<>();
906 final Deque<DetailAST> stack = new ArrayDeque<>();
907 while (vertex != null || !stack.isEmpty()) {
908 if (!stack.isEmpty()) {
909 vertex = stack.pop();
910 }
911 while (vertex != null) {
912 if (vertex.getType() == tokenType) {
913 result.add(vertex);
914 }
915 if (vertex.getNextSibling() != null) {
916 stack.push(vertex.getNextSibling());
917 }
918 vertex = vertex.getFirstChild();
919 }
920 }
921 return result;
922 }
923
924
925
926
927
928
929
930
931
932
933
934 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType,
935 int endLineNumber) {
936 DetailAST vertex = ast;
937 final Set<DetailAST> result = new HashSet<>();
938 final Deque<DetailAST> stack = new ArrayDeque<>();
939 while (vertex != null || !stack.isEmpty()) {
940 if (!stack.isEmpty()) {
941 vertex = stack.pop();
942 }
943 while (vertex != null) {
944 if (tokenType == vertex.getType()
945 && vertex.getLineNo() <= endLineNumber) {
946 result.add(vertex);
947 }
948 if (vertex.getNextSibling() != null) {
949 stack.push(vertex.getNextSibling());
950 }
951 vertex = vertex.getFirstChild();
952 }
953 }
954 return result;
955 }
956
957
958
959
960
961
962
963
964
965
966
967 private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token,
968 int endLineNumber) {
969 DetailAST vertex = ast;
970 final Set<DetailAST> result = new HashSet<>();
971 final Deque<DetailAST> stack = new ArrayDeque<>();
972 while (vertex != null || !stack.isEmpty()) {
973 if (!stack.isEmpty()) {
974 vertex = stack.pop();
975 }
976 while (vertex != null) {
977 if (isAstSimilar(token, vertex)
978 && vertex.getLineNo() <= endLineNumber) {
979 result.add(vertex);
980 }
981 if (vertex.getNextSibling() != null) {
982 stack.push(vertex.getNextSibling());
983 }
984 vertex = vertex.getFirstChild();
985 }
986 }
987 return result;
988 }
989
990
991
992
993
994
995
996
997
998 private AbstractFrame getMethodWithoutThis(DetailAST ast) {
999 AbstractFrame result = null;
1000 if (!validateOnlyOverlapping) {
1001 final AbstractFrame frame = findFrame(ast, LookMode.LOOK_FOR_METHOD);
1002 if (frame != null
1003 && ((ClassFrame) frame).hasInstanceMethod(ast)
1004 && !((ClassFrame) frame).hasStaticMethod(ast)) {
1005 result = frame;
1006 }
1007 }
1008 return result;
1009 }
1010
1011
1012
1013
1014
1015
1016
1017
1018 private AbstractFrame findClassFrame(DetailAST name, LookMode lookMode) {
1019 AbstractFrame frame = current.peek();
1020
1021 while (true) {
1022 frame = findFrame(frame, name, lookMode);
1023
1024 if (frame == null || frame instanceof ClassFrame) {
1025 break;
1026 }
1027
1028 frame = frame.getParent();
1029 }
1030
1031 return frame;
1032 }
1033
1034
1035
1036
1037
1038
1039
1040
1041 private AbstractFrame findFrame(DetailAST name, LookMode lookMode) {
1042 return findFrame(current.peek(), name, lookMode);
1043 }
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053 private static AbstractFrame findFrame(AbstractFrame frame, DetailAST name,
1054 LookMode lookMode) {
1055 return frame.getIfContains(name, lookMode);
1056 }
1057
1058
1059
1060
1061
1062
1063 private String getNearestClassFrameName() {
1064 AbstractFrame frame = current.peek();
1065 while (frame.getType() != FrameType.CLASS_FRAME) {
1066 frame = frame.getParent();
1067 }
1068 return frame.getFrameName();
1069 }
1070
1071
1072
1073
1074
1075
1076
1077 private static boolean isLambdaParameter(DetailAST ast) {
1078 boolean result = false;
1079 for (DetailAST parent = ast; parent != null; parent = parent.getParent()) {
1080 if (parent.getType() == TokenTypes.LAMBDA) {
1081 result = !isInsideTypeDefInsideLambda(ast)
1082 && isMatchingLambdaParam(ast, parent);
1083 break;
1084 }
1085 }
1086 return result;
1087 }
1088
1089
1090
1091
1092
1093
1094
1095
1096 private static boolean isMatchingLambdaParam(DetailAST ast, DetailAST lambda) {
1097 final boolean isMatchingParam;
1098 if (ast.getType() == TokenTypes.PARAMETER_DEF) {
1099 isMatchingParam = true;
1100 }
1101 else {
1102 final DetailAST lambdaParameters = lambda.findFirstToken(TokenTypes.PARAMETERS);
1103 if (lambdaParameters == null) {
1104 isMatchingParam = lambda.getFirstChild().getText().equals(ast.getText());
1105 }
1106 else {
1107 isMatchingParam = TokenUtil.findFirstTokenByPredicate(lambdaParameters,
1108 paramDef -> {
1109 final DetailAST param = paramDef.findFirstToken(TokenTypes.IDENT);
1110 return param != null && param.getText().equals(ast.getText());
1111 }).isPresent();
1112 }
1113 }
1114 return isMatchingParam;
1115 }
1116
1117
1118
1119
1120
1121
1122
1123
1124 private static boolean isInsideTypeDefInsideLambda(DetailAST ast) {
1125 boolean isInside = false;
1126 for (DetailAST parent = ast; parent.getType() != TokenTypes.LAMBDA;
1127 parent = parent.getParent()) {
1128 if (parent.getType() == TokenTypes.OBJBLOCK) {
1129 isInside = true;
1130 break;
1131 }
1132 }
1133 return isInside;
1134 }
1135
1136
1137
1138
1139
1140
1141
1142
1143 private static boolean isAstSimilar(DetailAST left, DetailAST right) {
1144 return left.getType() == right.getType() && left.getText().equals(right.getText());
1145 }
1146
1147
1148 private enum FrameType {
1149
1150
1151 CLASS_FRAME,
1152
1153 CTOR_FRAME,
1154
1155 METHOD_FRAME,
1156
1157 BLOCK_FRAME,
1158
1159 CATCH_FRAME,
1160
1161 FOR_FRAME,
1162
1163 TRY_WITH_RESOURCES_FRAME
1164
1165 }
1166
1167
1168
1169
1170 private enum LookMode {
1171
1172
1173 LOOK_FOR_METHOD,
1174
1175 NO_LOOK_FOR_METHOD
1176
1177 }
1178
1179
1180
1181
1182 private abstract static class AbstractFrame {
1183
1184
1185 private final Set<DetailAST> varIdents;
1186
1187
1188 private final AbstractFrame parent;
1189
1190
1191 private final DetailAST frameNameIdent;
1192
1193
1194
1195
1196
1197
1198
1199 protected AbstractFrame(AbstractFrame parent, DetailAST ident) {
1200 this.parent = parent;
1201 frameNameIdent = ident;
1202 varIdents = new HashSet<>();
1203 }
1204
1205
1206
1207
1208
1209
1210 public abstract FrameType getType();
1211
1212
1213
1214
1215
1216
1217 private void addIdent(DetailAST identToAdd) {
1218 varIdents.add(identToAdd);
1219 }
1220
1221
1222
1223
1224
1225
1226 public AbstractFrame getParent() {
1227 return parent;
1228 }
1229
1230
1231
1232
1233
1234
1235 public String getFrameName() {
1236 return frameNameIdent.getText();
1237 }
1238
1239
1240
1241
1242
1243
1244 public DetailAST getFrameNameIdent() {
1245 return frameNameIdent;
1246 }
1247
1248
1249
1250
1251
1252
1253
1254 public boolean containsFieldOrVariable(DetailAST identToFind) {
1255 return containsFieldOrVariableDef(varIdents, identToFind);
1256 }
1257
1258
1259
1260
1261
1262
1263
1264
1265 public AbstractFrame getIfContains(DetailAST identToFind, LookMode lookMode) {
1266 final AbstractFrame frame;
1267
1268 if (lookMode == LookMode.NO_LOOK_FOR_METHOD
1269 && containsFieldOrVariable(identToFind)) {
1270 frame = this;
1271 }
1272 else {
1273 frame = parent.getIfContains(identToFind, lookMode);
1274 }
1275 return frame;
1276 }
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287 public boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) {
1288 boolean result = false;
1289 for (DetailAST ast: set) {
1290 if (isProperDefinition(ident, ast)) {
1291 result = true;
1292 break;
1293 }
1294 }
1295 return result;
1296 }
1297
1298
1299
1300
1301
1302
1303
1304
1305 public boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1306 final String identToFind = ident.getText();
1307 return identToFind.equals(ast.getText())
1308 && CheckUtil.isBeforeInSource(ast, ident);
1309 }
1310 }
1311
1312
1313
1314
1315 private static class MethodFrame extends AbstractFrame {
1316
1317
1318
1319
1320
1321
1322
1323 MethodFrame(AbstractFrame parent, DetailAST ident) {
1324 super(parent, ident);
1325 }
1326
1327 @Override
1328 public FrameType getType() {
1329 return FrameType.METHOD_FRAME;
1330 }
1331
1332 }
1333
1334
1335
1336
1337 private static class ConstructorFrame extends AbstractFrame {
1338
1339
1340
1341
1342
1343
1344
1345 ConstructorFrame(AbstractFrame parent, DetailAST ident) {
1346 super(parent, ident);
1347 }
1348
1349 @Override
1350 public FrameType getType() {
1351 return FrameType.CTOR_FRAME;
1352 }
1353
1354 }
1355
1356
1357
1358
1359 private static class ClassFrame extends AbstractFrame {
1360
1361
1362 private final Set<DetailAST> instanceMembers;
1363
1364 private final Set<DetailAST> instanceMethods;
1365
1366 private final Set<DetailAST> staticMembers;
1367
1368 private final Set<DetailAST> staticMethods;
1369
1370
1371
1372
1373
1374
1375
1376 private ClassFrame(AbstractFrame parent, DetailAST ident) {
1377 super(parent, ident);
1378 instanceMembers = new HashSet<>();
1379 instanceMethods = new HashSet<>();
1380 staticMembers = new HashSet<>();
1381 staticMethods = new HashSet<>();
1382 }
1383
1384 @Override
1385 public FrameType getType() {
1386 return FrameType.CLASS_FRAME;
1387 }
1388
1389
1390
1391
1392
1393
1394 void addStaticMember(final DetailAST ident) {
1395 staticMembers.add(ident);
1396 }
1397
1398
1399
1400
1401
1402
1403 void addStaticMethod(final DetailAST ident) {
1404 staticMethods.add(ident);
1405 }
1406
1407
1408
1409
1410
1411
1412 void addInstanceMember(final DetailAST ident) {
1413 instanceMembers.add(ident);
1414 }
1415
1416
1417
1418
1419
1420
1421 void addInstanceMethod(final DetailAST ident) {
1422 instanceMethods.add(ident);
1423 }
1424
1425
1426
1427
1428
1429
1430
1431
1432 boolean hasInstanceMember(final DetailAST ident) {
1433 return containsFieldOrVariableDef(instanceMembers, ident);
1434 }
1435
1436
1437
1438
1439
1440
1441
1442
1443 boolean hasInstanceMethod(final DetailAST ident) {
1444 return containsMethodDef(instanceMethods, ident);
1445 }
1446
1447
1448
1449
1450
1451
1452
1453
1454 boolean hasStaticMethod(final DetailAST ident) {
1455 return containsMethodDef(staticMethods, ident);
1456 }
1457
1458
1459
1460
1461
1462
1463
1464 boolean hasFinalField(final DetailAST instanceMember) {
1465 boolean result = false;
1466 for (DetailAST member : instanceMembers) {
1467 final DetailAST parent = member.getParent();
1468 if (parent.getType() == TokenTypes.RECORD_COMPONENT_DEF) {
1469 result = true;
1470 }
1471 else {
1472 final DetailAST mods = parent.findFirstToken(TokenTypes.MODIFIERS);
1473 final boolean finalMod = mods.findFirstToken(TokenTypes.FINAL) != null;
1474 if (finalMod && isAstSimilar(member, instanceMember)) {
1475 result = true;
1476 }
1477 }
1478 }
1479 return result;
1480 }
1481
1482 @Override
1483 public boolean containsFieldOrVariable(DetailAST identToFind) {
1484 return containsFieldOrVariableDef(instanceMembers, identToFind)
1485 || containsFieldOrVariableDef(staticMembers, identToFind);
1486 }
1487
1488 @Override
1489 public boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1490 final String identToFind = ident.getText();
1491 return identToFind.equals(ast.getText());
1492 }
1493
1494
1495
1496
1497
1498
1499
1500
1501 @Override
1502 public AbstractFrame getIfContains(DetailAST identToFind, LookMode lookMode) {
1503 AbstractFrame frame = null;
1504
1505 if (containsMethod(identToFind)
1506 || containsFieldOrVariable(identToFind)) {
1507 frame = this;
1508 }
1509 else if (getParent() != null) {
1510 frame = getParent().getIfContains(identToFind, lookMode);
1511 }
1512 return frame;
1513 }
1514
1515
1516
1517
1518
1519
1520
1521 private boolean containsMethod(DetailAST methodToFind) {
1522 return containsMethodDef(instanceMethods, methodToFind)
1523 || containsMethodDef(staticMethods, methodToFind);
1524 }
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535 private static boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) {
1536 boolean result = false;
1537 for (DetailAST ast: set) {
1538 if (isSimilarSignature(ident, ast)) {
1539 result = true;
1540 break;
1541 }
1542 }
1543 return result;
1544 }
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554 private static boolean isSimilarSignature(DetailAST ident, DetailAST ast) {
1555 boolean result = false;
1556 final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST);
1557 if (elistToken != null && ident.getText().equals(ast.getText())) {
1558 final int paramsNumber =
1559 ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount();
1560 final int argsNumber = elistToken.getChildCount();
1561 result = paramsNumber == argsNumber;
1562 }
1563 return result;
1564 }
1565
1566 }
1567
1568
1569
1570
1571 private static class AnonymousClassFrame extends ClassFrame {
1572
1573
1574 private final String frameName;
1575
1576
1577
1578
1579
1580
1581
1582 AnonymousClassFrame(AbstractFrame parent, String frameName) {
1583 super(parent, null);
1584 this.frameName = frameName;
1585 }
1586
1587 @Override
1588 public String getFrameName() {
1589 return frameName;
1590 }
1591
1592 }
1593
1594
1595
1596
1597
1598
1599
1600 private static class CompactCompilationUnitFrame extends ClassFrame {
1601
1602
1603
1604
1605
1606
1607
1608 CompactCompilationUnitFrame(AbstractFrame parent, DetailAST ident) {
1609 super(parent, ident);
1610 }
1611
1612 }
1613
1614
1615
1616
1617 private static class BlockFrame extends AbstractFrame {
1618
1619
1620
1621
1622
1623
1624
1625 BlockFrame(AbstractFrame parent, DetailAST ident) {
1626 super(parent, ident);
1627 }
1628
1629 @Override
1630 public FrameType getType() {
1631 return FrameType.BLOCK_FRAME;
1632 }
1633
1634 }
1635
1636
1637
1638
1639 private static class CatchFrame extends AbstractFrame {
1640
1641
1642
1643
1644
1645
1646
1647 CatchFrame(AbstractFrame parent, DetailAST ident) {
1648 super(parent, ident);
1649 }
1650
1651 @Override
1652 public FrameType getType() {
1653 return FrameType.CATCH_FRAME;
1654 }
1655
1656
1657
1658
1659
1660
1661
1662
1663 @Override
1664 public AbstractFrame getIfContains(DetailAST identToFind, LookMode lookMode) {
1665 final AbstractFrame frame;
1666
1667 if (lookMode == LookMode.NO_LOOK_FOR_METHOD
1668 && containsFieldOrVariable(identToFind)) {
1669 frame = this;
1670 }
1671 else if (getParent().getType() == FrameType.TRY_WITH_RESOURCES_FRAME) {
1672
1673 frame = getParent().getParent().getIfContains(identToFind, lookMode);
1674 }
1675 else {
1676 frame = getParent().getIfContains(identToFind, lookMode);
1677 }
1678 return frame;
1679 }
1680
1681 }
1682
1683
1684
1685
1686 private static class ForFrame extends AbstractFrame {
1687
1688
1689
1690
1691
1692
1693
1694 ForFrame(AbstractFrame parent, DetailAST ident) {
1695 super(parent, ident);
1696 }
1697
1698 @Override
1699 public FrameType getType() {
1700 return FrameType.FOR_FRAME;
1701 }
1702
1703 }
1704
1705
1706
1707
1708
1709 private static class TryWithResourcesFrame extends AbstractFrame {
1710
1711
1712
1713
1714
1715
1716
1717 TryWithResourcesFrame(AbstractFrame parent, DetailAST ident) {
1718 super(parent, ident);
1719 }
1720
1721 @Override
1722 public FrameType getType() {
1723 return FrameType.TRY_WITH_RESOURCES_FRAME;
1724 }
1725
1726 }
1727
1728 }