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