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 boolean continueSearch = true;
448 while (continueSearch) {
449 continueSearch = false;
450 while (currentAst != null
451 && !TokenUtil.isOfType(currentAst, ANONYMOUS_CLASS_PARENT_TOKENS)) {
452 if (currentAst.getType() == TokenTypes.LAMBDA) {
453 topMostLambdaAst = currentAst;
454 currentAst = currentAst.getParent();
455 continueSearch = true;
456 break;
457 }
458 currentAst = currentAst.getParent();
459 result = currentAst;
460 }
461 }
462
463 if (currentAst == null) {
464 result = topMostLambdaAst;
465 }
466 return result;
467 }
468
469
470
471
472
473
474
475
476 private static void addLocalVariables(DetailAST varDefAst, Deque<VariableDesc> variablesStack) {
477 final DetailAST parentAst = varDefAst.getParent();
478 final DetailAST grandParent = parentAst.getParent();
479 final boolean isInstanceVarInInnerClass =
480 grandParent.getType() == TokenTypes.LITERAL_NEW
481 || grandParent.getType() == TokenTypes.CLASS_DEF;
482 if (isInstanceVarInInnerClass
483 || parentAst.getType() != TokenTypes.OBJBLOCK) {
484 final DetailAST ident = varDefAst.findFirstToken(TokenTypes.IDENT);
485 final VariableDesc desc = new VariableDesc(ident.getText(),
486 varDefAst.findFirstToken(TokenTypes.TYPE), findScopeOfVariable(varDefAst));
487 if (isInstanceVarInInnerClass) {
488 desc.registerAsInstOrClassVar();
489 }
490 variablesStack.push(desc);
491 }
492 }
493
494
495
496
497
498
499
500 private void addInstanceOrClassVar(DetailAST varDefAst) {
501 final DetailAST parentAst = varDefAst.getParent();
502 if (isNonLocalTypeDeclaration(parentAst.getParent())
503 && !isPrivateInstanceVariable(varDefAst)) {
504 final DetailAST ident = varDefAst.findFirstToken(TokenTypes.IDENT);
505 final VariableDesc desc = new VariableDesc(ident.getText());
506 typeDeclAstToTypeDeclDesc.get(parentAst.getParent()).addInstOrClassVar(desc);
507 }
508 }
509
510
511
512
513
514
515
516 private static boolean isPrivateInstanceVariable(DetailAST varDefAst) {
517 final AccessModifierOption varAccessModifier =
518 CheckUtil.getAccessModifierFromModifiersToken(varDefAst);
519 return varAccessModifier == AccessModifierOption.PRIVATE;
520 }
521
522
523
524
525
526
527
528 private TypeDeclDesc getSuperClassOfAnonInnerClass(DetailAST literalNewAst) {
529 TypeDeclDesc obtainedClass = null;
530 final String shortNameOfClass = CheckUtil.getShortNameOfAnonInnerClass(literalNewAst);
531 if (packageName != null && shortNameOfClass.startsWith(packageName)) {
532 final Optional<TypeDeclDesc> classWithCompletePackageName =
533 typeDeclAstToTypeDeclDesc.values()
534 .stream()
535 .filter(typeDeclDesc -> {
536 return typeDeclDesc.getQualifiedName().equals(shortNameOfClass);
537 })
538 .findFirst();
539 if (classWithCompletePackageName.isPresent()) {
540 obtainedClass = classWithCompletePackageName.orElseThrow();
541 }
542 }
543 else {
544 final List<TypeDeclDesc> typeDeclWithSameName = typeDeclWithSameName(shortNameOfClass);
545 if (!typeDeclWithSameName.isEmpty()) {
546 obtainedClass = getClosestMatchingTypeDeclaration(
547 anonInnerAstToTypeDeclDesc.get(literalNewAst).getQualifiedName(),
548 typeDeclWithSameName);
549 }
550 }
551 return obtainedClass;
552 }
553
554
555
556
557
558
559
560
561
562 private void modifyVariablesStack(TypeDeclDesc obtainedClass,
563 Deque<VariableDesc> variablesStack,
564 DetailAST literalNewAst) {
565 if (obtainedClass != null) {
566 final Deque<VariableDesc> instAndClassVarDeque = typeDeclAstToTypeDeclDesc
567 .get(obtainedClass.getTypeDeclAst())
568 .getUpdatedCopyOfVarStack(literalNewAst);
569 instAndClassVarDeque.forEach(variablesStack::push);
570 }
571 }
572
573
574
575
576
577
578
579 private List<TypeDeclDesc> typeDeclWithSameName(String superClassName) {
580 return typeDeclAstToTypeDeclDesc.values().stream()
581 .filter(typeDeclDesc -> {
582 return hasSameNameAsSuperClass(superClassName, typeDeclDesc);
583 })
584 .toList();
585 }
586
587
588
589
590
591
592
593
594
595 private boolean hasSameNameAsSuperClass(String superClassName, TypeDeclDesc typeDeclDesc) {
596 final boolean result;
597 if (packageName == null && typeDeclDesc.getDepth() == 0) {
598 result = typeDeclDesc.getQualifiedName().equals(superClassName);
599 }
600 else {
601 result = typeDeclDesc.getQualifiedName()
602 .endsWith(PACKAGE_SEPARATOR + superClassName);
603 }
604 return result;
605 }
606
607
608
609
610
611
612
613
614
615 private static TypeDeclDesc getClosestMatchingTypeDeclaration(String outerTypeDeclName,
616 List<TypeDeclDesc> typeDeclWithSameName) {
617 return Collections.min(typeDeclWithSameName, (first, second) -> {
618 return calculateTypeDeclarationDistance(outerTypeDeclName, first, second);
619 });
620 }
621
622
623
624
625
626
627
628
629
630
631 private static int calculateTypeDeclarationDistance(String outerTypeName,
632 TypeDeclDesc firstType,
633 TypeDeclDesc secondType) {
634 final int firstMatchCount =
635 countMatchingQualifierChars(outerTypeName, firstType.getQualifiedName());
636 final int secondMatchCount =
637 countMatchingQualifierChars(outerTypeName, secondType.getQualifiedName());
638 final int matchDistance = Integer.compare(secondMatchCount, firstMatchCount);
639
640 final int distance;
641 if (matchDistance == 0) {
642 distance = Integer.compare(firstType.getDepth(), secondType.getDepth());
643 }
644 else {
645 distance = matchDistance;
646 }
647
648 return distance;
649 }
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668 private static int countMatchingQualifierChars(String pattern,
669 String candidate) {
670 final int typeDeclarationToBeMatchedLength = candidate.length();
671 final int minLength = Math
672 .min(typeDeclarationToBeMatchedLength, pattern.length());
673 final boolean shouldCountBeUpdatedAtLastCharacter =
674 typeDeclarationToBeMatchedLength > minLength
675 && candidate.charAt(minLength) == PACKAGE_SEPARATOR.charAt(0);
676
677 int result = 0;
678 for (int idx = 0;
679 idx < minLength
680 && pattern.charAt(idx) == candidate.charAt(idx);
681 idx++) {
682
683 if (shouldCountBeUpdatedAtLastCharacter
684 || pattern.charAt(idx) == PACKAGE_SEPARATOR.charAt(0)) {
685 result = idx;
686 }
687 }
688 return result;
689 }
690
691
692
693
694
695
696
697 private String getQualifiedTypeDeclarationName(DetailAST typeDeclAst) {
698 final String className = typeDeclAst.findFirstToken(TokenTypes.IDENT).getText();
699 String outerClassQualifiedName = null;
700 if (!typeDeclarations.isEmpty()) {
701 outerClassQualifiedName = typeDeclarations.peek().getQualifiedName();
702 }
703 return CheckUtil
704 .getQualifiedTypeDeclarationName(packageName, outerClassQualifiedName, className);
705 }
706
707
708
709
710
711
712
713 private void iterateOverBlockContainingLocalAnonInnerClass(
714 DetailAST ast, Deque<VariableDesc> variablesStack) {
715 DetailAST currNode = ast;
716 while (currNode != null) {
717 customVisitToken(currNode, variablesStack);
718 DetailAST toVisit = currNode.getFirstChild();
719 while (currNode != ast && toVisit == null) {
720 customLeaveToken(currNode, variablesStack);
721 toVisit = currNode.getNextSibling();
722 currNode = currNode.getParent();
723 }
724 currNode = toVisit;
725 }
726 }
727
728
729
730
731
732
733
734
735 private void customVisitToken(DetailAST ast, Deque<VariableDesc> variablesStack) {
736 final int type = ast.getType();
737 switch (type) {
738 case TokenTypes.DOT -> visitDotToken(ast, variablesStack);
739
740 case TokenTypes.VARIABLE_DEF -> addLocalVariables(ast, variablesStack);
741
742 case TokenTypes.IDENT -> visitIdentToken(ast, variablesStack);
743
744 case TokenTypes.LITERAL_NEW -> {
745 if (ast.findFirstToken(TokenTypes.OBJBLOCK) != null) {
746 final TypeDeclDesc obtainedClass = getSuperClassOfAnonInnerClass(ast);
747 modifyVariablesStack(obtainedClass, variablesStack, ast);
748 }
749 }
750
751 default -> {
752
753 }
754 }
755 }
756
757
758
759
760
761
762
763
764 private void customLeaveToken(DetailAST ast, Deque<VariableDesc> variablesStack) {
765 logViolations(ast, variablesStack);
766 }
767
768
769
770
771
772
773
774 private static boolean shouldCheckIdentTokenNestedUnderDot(DetailAST dotAst) {
775
776 return TokenUtil.findFirstTokenByPredicate(dotAst,
777 childAst -> {
778 return TokenUtil.isOfType(childAst,
779 UNACCEPTABLE_CHILD_OF_DOT);
780 })
781 .isEmpty();
782 }
783
784
785
786
787
788
789
790 private static void checkIdentifierAst(DetailAST identAst, Deque<VariableDesc> variablesStack) {
791 for (VariableDesc variableDesc : variablesStack) {
792 if (identAst.getText().equals(variableDesc.getName())
793 && !isLeftHandSideValue(identAst)) {
794 variableDesc.registerAsUsed();
795 break;
796 }
797 }
798 }
799
800
801
802
803
804
805
806 private static DetailAST findScopeOfVariable(DetailAST variableDef) {
807 final DetailAST result;
808 final DetailAST parentAst = variableDef.getParent();
809 if (TokenUtil.isOfType(parentAst, TokenTypes.SLIST, TokenTypes.OBJBLOCK)) {
810 result = parentAst;
811 }
812 else {
813 result = parentAst.getParent();
814 }
815 return result;
816 }
817
818
819
820
821
822
823
824
825
826
827 private static boolean isLeftHandSideValue(DetailAST identAst) {
828 final DetailAST parent = identAst.getParent();
829 return isStandAloneIncrementOrDecrement(identAst)
830 || parent.getType() == TokenTypes.ASSIGN
831 && identAst != parent.getLastChild();
832 }
833
834
835
836
837
838
839
840
841
842 private static boolean isStandAloneIncrementOrDecrement(DetailAST identAst) {
843 final DetailAST parent = identAst.getParent();
844 final DetailAST grandParent = parent.getParent();
845 return TokenUtil.isOfType(parent, INCREMENT_AND_DECREMENT_TOKENS)
846 && TokenUtil.isOfType(grandParent, TokenTypes.EXPR)
847 && !isIncrementOrDecrementVariableUsed(grandParent);
848 }
849
850
851
852
853
854
855
856
857
858 private static boolean isIncrementOrDecrementVariableUsed(DetailAST exprAst) {
859 return TokenUtil.isOfType(exprAst.getParent(), INCREMENT_DECREMENT_VARIABLE_USAGE_TYPES)
860 && exprAst.getParent().getParent().getType() != TokenTypes.FOR_ITERATOR;
861 }
862
863
864
865
866 private static final class VariableDesc {
867
868
869
870
871 private final String name;
872
873
874
875
876 private final DetailAST typeAst;
877
878
879
880
881
882
883 private final DetailAST scope;
884
885
886
887
888 private boolean instVarOrClassVar;
889
890
891
892
893 private boolean used;
894
895
896
897
898
899
900
901
902
903
904 private VariableDesc(String name, DetailAST typeAst, DetailAST scope) {
905 this.name = name;
906 this.typeAst = typeAst;
907 this.scope = scope;
908 }
909
910
911
912
913
914
915 private VariableDesc(String name) {
916 this(name, null, null);
917 }
918
919
920
921
922
923
924
925
926
927 private VariableDesc(String name, DetailAST scope) {
928 this(name, null, scope);
929 }
930
931
932
933
934
935
936 String getName() {
937 return name;
938 }
939
940
941
942
943
944
945 DetailAST getTypeAst() {
946 return typeAst;
947 }
948
949
950
951
952
953
954
955
956 DetailAST getScope() {
957 return scope;
958 }
959
960
961
962
963 void registerAsUsed() {
964 used = true;
965 }
966
967
968
969
970
971 void registerAsInstOrClassVar() {
972 instVarOrClassVar = true;
973 }
974
975
976
977
978
979
980 boolean isUsed() {
981 return used;
982 }
983
984
985
986
987
988
989 boolean isInstVarOrClassVar() {
990 return instVarOrClassVar;
991 }
992 }
993
994
995
996
997
998
999
1000 private static final class TypeDeclDesc {
1001
1002
1003
1004
1005 private final String qualifiedName;
1006
1007
1008
1009
1010 private final int depth;
1011
1012
1013
1014
1015 private final DetailAST typeDeclAst;
1016
1017
1018
1019
1020 private final Deque<VariableDesc> instanceAndClassVarStack;
1021
1022
1023
1024
1025
1026
1027
1028
1029 private TypeDeclDesc(String qualifiedName, int depth,
1030 DetailAST typeDeclAst) {
1031 this.qualifiedName = qualifiedName;
1032 this.depth = depth;
1033 this.typeDeclAst = typeDeclAst;
1034 instanceAndClassVarStack = new ArrayDeque<>();
1035 }
1036
1037
1038
1039
1040
1041
1042
1043 String getQualifiedName() {
1044 return qualifiedName;
1045 }
1046
1047
1048
1049
1050
1051
1052 int getDepth() {
1053 return depth;
1054 }
1055
1056
1057
1058
1059
1060
1061 DetailAST getTypeDeclAst() {
1062 return typeDeclAst;
1063 }
1064
1065
1066
1067
1068
1069
1070
1071 Deque<VariableDesc> getUpdatedCopyOfVarStack(DetailAST literalNewAst) {
1072 final DetailAST updatedScope = literalNewAst;
1073 final Deque<VariableDesc> instAndClassVarDeque = new ArrayDeque<>();
1074 instanceAndClassVarStack.forEach(instVar -> {
1075 final VariableDesc variableDesc = new VariableDesc(instVar.getName(),
1076 updatedScope);
1077 variableDesc.registerAsInstOrClassVar();
1078 instAndClassVarDeque.push(variableDesc);
1079 });
1080 return instAndClassVarDeque;
1081 }
1082
1083
1084
1085
1086
1087
1088 void addInstOrClassVar(VariableDesc variableDesc) {
1089 instanceAndClassVarStack.push(variableDesc);
1090 }
1091 }
1092 }