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 }