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