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.Collections;
24 import java.util.Deque;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.LinkedHashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Optional;
31 import java.util.Set;
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.checks.naming.AccessModifierOption;
38 import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
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
77
78
79
80
81 @FileStatefulCheck
82 public class UnusedLocalVariableCheck extends AbstractCheck {
83
84
85
86
87
88 public static final String MSG_UNUSED_LOCAL_VARIABLE = "unused.local.var";
89
90
91
92
93
94 public static final String MSG_UNUSED_NAMED_LOCAL_VARIABLE = "unused.named.local.var";
95
96
97
98
99 private static final int[] INCREMENT_AND_DECREMENT_TOKENS = {
100 TokenTypes.POST_INC,
101 TokenTypes.POST_DEC,
102 TokenTypes.INC,
103 TokenTypes.DEC,
104 };
105
106
107
108
109 private static final int[] SCOPES = {
110 TokenTypes.SLIST,
111 TokenTypes.LITERAL_FOR,
112 TokenTypes.OBJBLOCK,
113 };
114
115
116
117
118 private static final int[] UNACCEPTABLE_CHILD_OF_DOT = {
119 TokenTypes.DOT,
120 TokenTypes.METHOD_CALL,
121 TokenTypes.LITERAL_NEW,
122 TokenTypes.LITERAL_SUPER,
123 TokenTypes.LITERAL_CLASS,
124 TokenTypes.LITERAL_THIS,
125 };
126
127
128
129
130 private static final int[] UNACCEPTABLE_PARENT_OF_IDENT = {
131 TokenTypes.VARIABLE_DEF,
132 TokenTypes.DOT,
133 TokenTypes.LITERAL_NEW,
134 TokenTypes.PATTERN_VARIABLE_DEF,
135 TokenTypes.METHOD_CALL,
136 TokenTypes.TYPE,
137 };
138
139
140
141
142 private static final int[] ANONYMOUS_CLASS_PARENT_TOKENS = {
143 TokenTypes.METHOD_DEF,
144 TokenTypes.CTOR_DEF,
145 TokenTypes.STATIC_INIT,
146 TokenTypes.INSTANCE_INIT,
147 TokenTypes.COMPACT_CTOR_DEF,
148 };
149
150
151
152
153
154
155
156
157
158 private static final int[] INCREMENT_DECREMENT_VARIABLE_USAGE_TYPES = {
159 TokenTypes.ELIST,
160 TokenTypes.INDEX_OP,
161 TokenTypes.ASSIGN,
162 TokenTypes.LITERAL_SWITCH,
163 };
164
165
166 private static final String PACKAGE_SEPARATOR = ".";
167
168
169
170
171 private final Deque<VariableDesc> variables = new ArrayDeque<>();
172
173
174
175
176
177
178 private final Deque<TypeDeclDesc> typeDeclarations = new ArrayDeque<>();
179
180
181
182
183 private final Map<DetailAST, TypeDeclDesc> typeDeclAstToTypeDeclDesc = new LinkedHashMap<>();
184
185
186
187
188
189 private final Map<DetailAST, TypeDeclDesc> anonInnerAstToTypeDeclDesc = new HashMap<>();
190
191
192
193
194
195 private final Set<DetailAST> anonInnerClassHolders = new HashSet<>();
196
197
198
199
200
201
202 private boolean allowUnnamedVariables = true;
203
204
205
206
207 private String packageName;
208
209
210
211
212 private int depth;
213
214
215
216
217
218
219
220
221
222 public void setAllowUnnamedVariables(boolean allowUnnamedVariables) {
223 this.allowUnnamedVariables = allowUnnamedVariables;
224 }
225
226 @Override
227 public int[] getDefaultTokens() {
228 return new int[] {
229 TokenTypes.DOT,
230 TokenTypes.VARIABLE_DEF,
231 TokenTypes.IDENT,
232 TokenTypes.SLIST,
233 TokenTypes.LITERAL_FOR,
234 TokenTypes.OBJBLOCK,
235 TokenTypes.CLASS_DEF,
236 TokenTypes.INTERFACE_DEF,
237 TokenTypes.ANNOTATION_DEF,
238 TokenTypes.PACKAGE_DEF,
239 TokenTypes.LITERAL_NEW,
240 TokenTypes.METHOD_DEF,
241 TokenTypes.CTOR_DEF,
242 TokenTypes.STATIC_INIT,
243 TokenTypes.INSTANCE_INIT,
244 TokenTypes.COMPILATION_UNIT,
245 TokenTypes.LAMBDA,
246 TokenTypes.ENUM_DEF,
247 TokenTypes.RECORD_DEF,
248 TokenTypes.COMPACT_CTOR_DEF,
249 };
250 }
251
252 @Override
253 public int[] getAcceptableTokens() {
254 return getDefaultTokens();
255 }
256
257 @Override
258 public int[] getRequiredTokens() {
259 return getDefaultTokens();
260 }
261
262 @Override
263 public void beginTree(DetailAST root) {
264 variables.clear();
265 typeDeclarations.clear();
266 typeDeclAstToTypeDeclDesc.clear();
267 anonInnerAstToTypeDeclDesc.clear();
268 anonInnerClassHolders.clear();
269 packageName = null;
270 depth = 0;
271 }
272
273 @Override
274 public void visitToken(DetailAST ast) {
275 final int type = ast.getType();
276 if (type == TokenTypes.DOT) {
277 visitDotToken(ast, variables);
278 }
279 else if (type == TokenTypes.VARIABLE_DEF && !skipUnnamedVariables(ast)) {
280 visitVariableDefToken(ast);
281 }
282 else if (type == TokenTypes.IDENT) {
283 visitIdentToken(ast, variables);
284 }
285 else if (isInsideLocalAnonInnerClass(ast)) {
286 visitLocalAnonInnerClass(ast);
287 }
288 else if (isNonLocalTypeDeclaration(ast)) {
289 visitNonLocalTypeDeclarationToken(ast);
290 }
291 else if (type == TokenTypes.PACKAGE_DEF) {
292 packageName = CheckUtil.extractQualifiedName(ast.getFirstChild().getNextSibling());
293 }
294 }
295
296 @Override
297 public void leaveToken(DetailAST ast) {
298 if (TokenUtil.isOfType(ast, SCOPES)) {
299 logViolations(ast, variables);
300 }
301 else if (ast.getType() == TokenTypes.COMPILATION_UNIT) {
302 leaveCompilationUnit();
303 }
304 else if (isNonLocalTypeDeclaration(ast)) {
305 depth--;
306 typeDeclarations.pop();
307 }
308 }
309
310
311
312
313
314
315
316 private static void visitDotToken(DetailAST dotAst, Deque<VariableDesc> variablesStack) {
317 if (dotAst.getParent().getType() != TokenTypes.LITERAL_NEW
318 && shouldCheckIdentTokenNestedUnderDot(dotAst)) {
319 final DetailAST identifier = dotAst.findFirstToken(TokenTypes.IDENT);
320 if (identifier != null) {
321 checkIdentifierAst(identifier, variablesStack);
322 }
323 }
324 }
325
326
327
328
329
330
331 private void visitVariableDefToken(DetailAST varDefAst) {
332 addLocalVariables(varDefAst, variables);
333 addInstanceOrClassVar(varDefAst);
334 }
335
336
337
338
339
340
341
342 private static void visitIdentToken(DetailAST identAst, Deque<VariableDesc> variablesStack) {
343 final DetailAST parent = identAst.getParent();
344 final boolean isMethodReferenceMethodName = parent.getType() == TokenTypes.METHOD_REF
345 && parent.getFirstChild() != identAst;
346 final boolean isConstructorReference = parent.getType() == TokenTypes.METHOD_REF
347 && parent.getLastChild().getType() == TokenTypes.LITERAL_NEW;
348 final boolean isNestedClassInitialization =
349 TokenUtil.isOfType(identAst.getNextSibling(), TokenTypes.LITERAL_NEW)
350 && parent.getType() == TokenTypes.DOT;
351
352 if (isNestedClassInitialization || !isMethodReferenceMethodName
353 && !isConstructorReference
354 && !TokenUtil.isOfType(parent, UNACCEPTABLE_PARENT_OF_IDENT)) {
355 checkIdentifierAst(identAst, variablesStack);
356 }
357 }
358
359
360
361
362
363
364 private void visitNonLocalTypeDeclarationToken(DetailAST typeDeclAst) {
365 final String qualifiedName = getQualifiedTypeDeclarationName(typeDeclAst);
366 final TypeDeclDesc currTypeDecl = new TypeDeclDesc(qualifiedName, depth, typeDeclAst);
367 depth++;
368 typeDeclarations.push(currTypeDecl);
369 typeDeclAstToTypeDeclDesc.put(typeDeclAst, currTypeDecl);
370 }
371
372
373
374
375
376
377 private void visitLocalAnonInnerClass(DetailAST literalNewAst) {
378 anonInnerAstToTypeDeclDesc.put(literalNewAst, typeDeclarations.peek());
379 anonInnerClassHolders.add(getBlockContainingLocalAnonInnerClass(literalNewAst));
380 }
381
382
383
384
385
386
387
388
389 private boolean skipUnnamedVariables(DetailAST varDefAst) {
390 final DetailAST ident = varDefAst.findFirstToken(TokenTypes.IDENT);
391 return allowUnnamedVariables && "_".equals(ident.getText());
392 }
393
394
395
396
397
398
399
400
401 private static boolean isInsideLocalAnonInnerClass(DetailAST literalNewAst) {
402 boolean result = false;
403 final DetailAST lastChild = literalNewAst.getLastChild();
404 if (lastChild != null && lastChild.getType() == TokenTypes.OBJBLOCK) {
405 DetailAST currentAst = literalNewAst;
406 while (!TokenUtil.isTypeDeclaration(currentAst.getType())) {
407 if (currentAst.getType() == TokenTypes.SLIST) {
408 result = true;
409 break;
410 }
411 currentAst = currentAst.getParent();
412 }
413 }
414 return result;
415 }
416
417
418
419
420
421
422
423 private void logViolations(DetailAST scopeAst, Deque<VariableDesc> variablesStack) {
424 while (!variablesStack.isEmpty() && variablesStack.peek().getScope() == scopeAst) {
425 final VariableDesc variableDesc = variablesStack.pop();
426 if (!variableDesc.isUsed()
427 && !variableDesc.isInstVarOrClassVar()) {
428 final DetailAST typeAst = variableDesc.getTypeAst();
429 if (allowUnnamedVariables) {
430 log(typeAst, MSG_UNUSED_NAMED_LOCAL_VARIABLE, variableDesc.getName());
431 }
432 else {
433 log(typeAst, MSG_UNUSED_LOCAL_VARIABLE, variableDesc.getName());
434 }
435 }
436 }
437 }
438
439
440
441
442
443
444
445 private void leaveCompilationUnit() {
446 anonInnerClassHolders.forEach(holder -> {
447 iterateOverBlockContainingLocalAnonInnerClass(holder, new ArrayDeque<>());
448 });
449 }
450
451
452
453
454
455
456
457 private static boolean isNonLocalTypeDeclaration(DetailAST typeDeclAst) {
458 return TokenUtil.isTypeDeclaration(typeDeclAst.getType())
459 && typeDeclAst.getParent().getType() != TokenTypes.SLIST;
460 }
461
462
463
464
465
466
467
468 private static DetailAST getBlockContainingLocalAnonInnerClass(DetailAST literalNewAst) {
469 DetailAST currentAst = literalNewAst;
470 DetailAST result = null;
471 DetailAST topMostLambdaAst = null;
472 while (currentAst != null && !TokenUtil.isOfType(currentAst,
473 ANONYMOUS_CLASS_PARENT_TOKENS)) {
474 if (currentAst.getType() == TokenTypes.LAMBDA) {
475 topMostLambdaAst = currentAst;
476 }
477 currentAst = currentAst.getParent();
478 result = currentAst;
479 }
480
481 if (currentAst == null) {
482 result = topMostLambdaAst;
483 }
484 return result;
485 }
486
487
488
489
490
491
492
493
494 private static void addLocalVariables(DetailAST varDefAst, Deque<VariableDesc> variablesStack) {
495 final DetailAST parentAst = varDefAst.getParent();
496 final DetailAST grandParent = parentAst.getParent();
497 final boolean isInstanceVarInInnerClass =
498 grandParent.getType() == TokenTypes.LITERAL_NEW
499 || grandParent.getType() == TokenTypes.CLASS_DEF;
500 if (isInstanceVarInInnerClass
501 || parentAst.getType() != TokenTypes.OBJBLOCK) {
502 final DetailAST ident = varDefAst.findFirstToken(TokenTypes.IDENT);
503 final VariableDesc desc = new VariableDesc(ident.getText(),
504 varDefAst.findFirstToken(TokenTypes.TYPE), findScopeOfVariable(varDefAst));
505 if (isInstanceVarInInnerClass) {
506 desc.registerAsInstOrClassVar();
507 }
508 variablesStack.push(desc);
509 }
510 }
511
512
513
514
515
516
517
518 private void addInstanceOrClassVar(DetailAST varDefAst) {
519 final DetailAST parentAst = varDefAst.getParent();
520 if (isNonLocalTypeDeclaration(parentAst.getParent())
521 && !isPrivateInstanceVariable(varDefAst)) {
522 final DetailAST ident = varDefAst.findFirstToken(TokenTypes.IDENT);
523 final VariableDesc desc = new VariableDesc(ident.getText());
524 typeDeclAstToTypeDeclDesc.get(parentAst.getParent()).addInstOrClassVar(desc);
525 }
526 }
527
528
529
530
531
532
533
534 private static boolean isPrivateInstanceVariable(DetailAST varDefAst) {
535 final AccessModifierOption varAccessModifier =
536 CheckUtil.getAccessModifierFromModifiersToken(varDefAst);
537 return varAccessModifier == AccessModifierOption.PRIVATE;
538 }
539
540
541
542
543
544
545
546 private TypeDeclDesc getSuperClassOfAnonInnerClass(DetailAST literalNewAst) {
547 TypeDeclDesc obtainedClass = null;
548 final String shortNameOfClass = CheckUtil.getShortNameOfAnonInnerClass(literalNewAst);
549 if (packageName != null && shortNameOfClass.startsWith(packageName)) {
550 final Optional<TypeDeclDesc> classWithCompletePackageName =
551 typeDeclAstToTypeDeclDesc.values()
552 .stream()
553 .filter(typeDeclDesc -> {
554 return typeDeclDesc.getQualifiedName().equals(shortNameOfClass);
555 })
556 .findFirst();
557 if (classWithCompletePackageName.isPresent()) {
558 obtainedClass = classWithCompletePackageName.orElseThrow();
559 }
560 }
561 else {
562 final List<TypeDeclDesc> typeDeclWithSameName = typeDeclWithSameName(shortNameOfClass);
563 if (!typeDeclWithSameName.isEmpty()) {
564 obtainedClass = getClosestMatchingTypeDeclaration(
565 anonInnerAstToTypeDeclDesc.get(literalNewAst).getQualifiedName(),
566 typeDeclWithSameName);
567 }
568 }
569 return obtainedClass;
570 }
571
572
573
574
575
576
577
578
579
580 private void modifyVariablesStack(TypeDeclDesc obtainedClass,
581 Deque<VariableDesc> variablesStack,
582 DetailAST literalNewAst) {
583 if (obtainedClass != null) {
584 final Deque<VariableDesc> instAndClassVarDeque = typeDeclAstToTypeDeclDesc
585 .get(obtainedClass.getTypeDeclAst())
586 .getUpdatedCopyOfVarStack(literalNewAst);
587 instAndClassVarDeque.forEach(variablesStack::push);
588 }
589 }
590
591
592
593
594
595
596
597 private List<TypeDeclDesc> typeDeclWithSameName(String superClassName) {
598 return typeDeclAstToTypeDeclDesc.values().stream()
599 .filter(typeDeclDesc -> {
600 return hasSameNameAsSuperClass(superClassName, typeDeclDesc);
601 })
602 .toList();
603 }
604
605
606
607
608
609
610
611
612
613 private boolean hasSameNameAsSuperClass(String superClassName, TypeDeclDesc typeDeclDesc) {
614 final boolean result;
615 if (packageName == null && typeDeclDesc.getDepth() == 0) {
616 result = typeDeclDesc.getQualifiedName().equals(superClassName);
617 }
618 else {
619 result = typeDeclDesc.getQualifiedName()
620 .endsWith(PACKAGE_SEPARATOR + superClassName);
621 }
622 return result;
623 }
624
625
626
627
628
629
630
631
632
633 private static TypeDeclDesc getClosestMatchingTypeDeclaration(String outerTypeDeclName,
634 List<TypeDeclDesc> typeDeclWithSameName) {
635 return Collections.min(typeDeclWithSameName, (first, second) -> {
636 return calculateTypeDeclarationDistance(outerTypeDeclName, first, second);
637 });
638 }
639
640
641
642
643
644
645
646
647
648
649 private static int calculateTypeDeclarationDistance(String outerTypeName,
650 TypeDeclDesc firstType,
651 TypeDeclDesc secondType) {
652 final int firstMatchCount =
653 countMatchingQualifierChars(outerTypeName, firstType.getQualifiedName());
654 final int secondMatchCount =
655 countMatchingQualifierChars(outerTypeName, secondType.getQualifiedName());
656 final int matchDistance = Integer.compare(secondMatchCount, firstMatchCount);
657
658 final int distance;
659 if (matchDistance == 0) {
660 distance = Integer.compare(firstType.getDepth(), secondType.getDepth());
661 }
662 else {
663 distance = matchDistance;
664 }
665
666 return distance;
667 }
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686 private static int countMatchingQualifierChars(String pattern,
687 String candidate) {
688 final int typeDeclarationToBeMatchedLength = candidate.length();
689 final int minLength = Math
690 .min(typeDeclarationToBeMatchedLength, pattern.length());
691 final boolean shouldCountBeUpdatedAtLastCharacter =
692 typeDeclarationToBeMatchedLength > minLength
693 && candidate.charAt(minLength) == PACKAGE_SEPARATOR.charAt(0);
694
695 int result = 0;
696 for (int idx = 0;
697 idx < minLength
698 && pattern.charAt(idx) == candidate.charAt(idx);
699 idx++) {
700
701 if (shouldCountBeUpdatedAtLastCharacter
702 || pattern.charAt(idx) == PACKAGE_SEPARATOR.charAt(0)) {
703 result = idx;
704 }
705 }
706 return result;
707 }
708
709
710
711
712
713
714
715 private String getQualifiedTypeDeclarationName(DetailAST typeDeclAst) {
716 final String className = typeDeclAst.findFirstToken(TokenTypes.IDENT).getText();
717 String outerClassQualifiedName = null;
718 if (!typeDeclarations.isEmpty()) {
719 outerClassQualifiedName = typeDeclarations.peek().getQualifiedName();
720 }
721 return CheckUtil
722 .getQualifiedTypeDeclarationName(packageName, outerClassQualifiedName, className);
723 }
724
725
726
727
728
729
730
731 private void iterateOverBlockContainingLocalAnonInnerClass(
732 DetailAST ast, Deque<VariableDesc> variablesStack) {
733 DetailAST currNode = ast;
734 while (currNode != null) {
735 customVisitToken(currNode, variablesStack);
736 DetailAST toVisit = currNode.getFirstChild();
737 while (currNode != ast && toVisit == null) {
738 customLeaveToken(currNode, variablesStack);
739 toVisit = currNode.getNextSibling();
740 currNode = currNode.getParent();
741 }
742 currNode = toVisit;
743 }
744 }
745
746
747
748
749
750
751
752
753 private void customVisitToken(DetailAST ast, Deque<VariableDesc> variablesStack) {
754 final int type = ast.getType();
755 if (type == TokenTypes.DOT) {
756 visitDotToken(ast, variablesStack);
757 }
758 else if (type == TokenTypes.VARIABLE_DEF) {
759 addLocalVariables(ast, variablesStack);
760 }
761 else if (type == TokenTypes.IDENT) {
762 visitIdentToken(ast, variablesStack);
763 }
764 else if (isInsideLocalAnonInnerClass(ast)) {
765 final TypeDeclDesc obtainedClass = getSuperClassOfAnonInnerClass(ast);
766 modifyVariablesStack(obtainedClass, variablesStack, ast);
767 }
768 }
769
770
771
772
773
774
775
776
777 private void customLeaveToken(DetailAST ast, Deque<VariableDesc> variablesStack) {
778 logViolations(ast, variablesStack);
779 }
780
781
782
783
784
785
786
787 private static boolean shouldCheckIdentTokenNestedUnderDot(DetailAST dotAst) {
788
789 return TokenUtil.findFirstTokenByPredicate(dotAst,
790 childAst -> {
791 return TokenUtil.isOfType(childAst,
792 UNACCEPTABLE_CHILD_OF_DOT);
793 })
794 .isEmpty();
795 }
796
797
798
799
800
801
802
803 private static void checkIdentifierAst(DetailAST identAst, Deque<VariableDesc> variablesStack) {
804 for (VariableDesc variableDesc : variablesStack) {
805 if (identAst.getText().equals(variableDesc.getName())
806 && !isLeftHandSideValue(identAst)) {
807 variableDesc.registerAsUsed();
808 break;
809 }
810 }
811 }
812
813
814
815
816
817
818
819 private static DetailAST findScopeOfVariable(DetailAST variableDef) {
820 final DetailAST result;
821 final DetailAST parentAst = variableDef.getParent();
822 if (TokenUtil.isOfType(parentAst, TokenTypes.SLIST, TokenTypes.OBJBLOCK)) {
823 result = parentAst;
824 }
825 else {
826 result = parentAst.getParent();
827 }
828 return result;
829 }
830
831
832
833
834
835
836
837
838
839
840 private static boolean isLeftHandSideValue(DetailAST identAst) {
841 final DetailAST parent = identAst.getParent();
842 return isStandAloneIncrementOrDecrement(identAst)
843 || parent.getType() == TokenTypes.ASSIGN
844 && identAst != parent.getLastChild();
845 }
846
847
848
849
850
851
852
853
854
855 private static boolean isStandAloneIncrementOrDecrement(DetailAST identAst) {
856 final DetailAST parent = identAst.getParent();
857 final DetailAST grandParent = parent.getParent();
858 return TokenUtil.isOfType(parent, INCREMENT_AND_DECREMENT_TOKENS)
859 && TokenUtil.isOfType(grandParent, TokenTypes.EXPR)
860 && !isIncrementOrDecrementVariableUsed(grandParent);
861 }
862
863
864
865
866
867
868
869
870
871 private static boolean isIncrementOrDecrementVariableUsed(DetailAST exprAst) {
872 return TokenUtil.isOfType(exprAst.getParent(), INCREMENT_DECREMENT_VARIABLE_USAGE_TYPES)
873 && exprAst.getParent().getParent().getType() != TokenTypes.FOR_ITERATOR;
874 }
875
876
877
878
879 private static final class VariableDesc {
880
881
882
883
884 private final String name;
885
886
887
888
889 private final DetailAST typeAst;
890
891
892
893
894
895
896 private final DetailAST scope;
897
898
899
900
901 private boolean instVarOrClassVar;
902
903
904
905
906 private boolean used;
907
908
909
910
911
912
913
914
915
916
917 private VariableDesc(String name, DetailAST typeAst, DetailAST scope) {
918 this.name = name;
919 this.typeAst = typeAst;
920 this.scope = scope;
921 }
922
923
924
925
926
927
928 private VariableDesc(String name) {
929 this(name, null, null);
930 }
931
932
933
934
935
936
937
938
939
940 private VariableDesc(String name, DetailAST scope) {
941 this(name, null, scope);
942 }
943
944
945
946
947
948
949 public String getName() {
950 return name;
951 }
952
953
954
955
956
957
958 public DetailAST getTypeAst() {
959 return typeAst;
960 }
961
962
963
964
965
966
967
968
969 public DetailAST getScope() {
970 return scope;
971 }
972
973
974
975
976 public void registerAsUsed() {
977 used = true;
978 }
979
980
981
982
983
984 public void registerAsInstOrClassVar() {
985 instVarOrClassVar = true;
986 }
987
988
989
990
991
992
993 public boolean isUsed() {
994 return used;
995 }
996
997
998
999
1000
1001
1002 public boolean isInstVarOrClassVar() {
1003 return instVarOrClassVar;
1004 }
1005 }
1006
1007
1008
1009
1010
1011
1012
1013 private static final class TypeDeclDesc {
1014
1015
1016
1017
1018 private final String qualifiedName;
1019
1020
1021
1022
1023 private final int depth;
1024
1025
1026
1027
1028 private final DetailAST typeDeclAst;
1029
1030
1031
1032
1033 private final Deque<VariableDesc> instanceAndClassVarStack;
1034
1035
1036
1037
1038
1039
1040
1041
1042 private TypeDeclDesc(String qualifiedName, int depth,
1043 DetailAST typeDeclAst) {
1044 this.qualifiedName = qualifiedName;
1045 this.depth = depth;
1046 this.typeDeclAst = typeDeclAst;
1047 instanceAndClassVarStack = new ArrayDeque<>();
1048 }
1049
1050
1051
1052
1053
1054
1055
1056 public String getQualifiedName() {
1057 return qualifiedName;
1058 }
1059
1060
1061
1062
1063
1064
1065 public int getDepth() {
1066 return depth;
1067 }
1068
1069
1070
1071
1072
1073
1074 public DetailAST getTypeDeclAst() {
1075 return typeDeclAst;
1076 }
1077
1078
1079
1080
1081
1082
1083
1084 public Deque<VariableDesc> getUpdatedCopyOfVarStack(DetailAST literalNewAst) {
1085 final DetailAST updatedScope = literalNewAst;
1086 final Deque<VariableDesc> instAndClassVarDeque = new ArrayDeque<>();
1087 instanceAndClassVarStack.forEach(instVar -> {
1088 final VariableDesc variableDesc = new VariableDesc(instVar.getName(),
1089 updatedScope);
1090 variableDesc.registerAsInstOrClassVar();
1091 instAndClassVarDeque.push(variableDesc);
1092 });
1093 return instAndClassVarDeque;
1094 }
1095
1096
1097
1098
1099
1100
1101 public void addInstOrClassVar(VariableDesc variableDesc) {
1102 instanceAndClassVarStack.push(variableDesc);
1103 }
1104 }
1105 }