View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.ArrayDeque;
23  import java.util.BitSet;
24  import java.util.Deque;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.LinkedList;
28  import java.util.Map;
29  import java.util.Queue;
30  import java.util.Set;
31  
32  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
33  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
34  import com.puppycrawl.tools.checkstyle.api.DetailAST;
35  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
36  import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
37  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
38  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
39  
40  /**
41   * <div>
42   * Checks that references to instance variables and methods of the present
43   * object are explicitly of the form "this.varName" or "this.methodName(args)"
44   * and that those references don't rely on the default behavior when "this." is absent.
45   * </div>
46   *
47   * <p>Warning: the Check is very controversial if 'validateOnlyOverlapping' option is set to 'false'
48   * and not that actual nowadays.</p>
49   *
50   * <p>Rationale:</p>
51   * <ol>
52   *   <li>
53   *     The same notation/habit for C++ and Java (C++ have global methods, so having
54   *     &quot;this.&quot; do make sense in it to distinguish call of method of class
55   *     instead of global).
56   *   </li>
57   *   <li>
58   *     Non-IDE development (ease of refactoring, some clearness to distinguish
59   *     static and non-static methods).
60   *   </li>
61   * </ol>
62   *
63   * <p>Limitations: Nothing is currently done about static variables
64   * or catch-blocks.  Static methods invoked on a class name seem to be OK;
65   * both the class name and the method name have a DOT parent.
66   * Non-static methods invoked on either this or a variable name seem to be
67   * OK, likewise.
68   * </p>
69   * <ul>
70   * <li>
71   * Property {@code checkFields} - Control whether to check references to fields.
72   * Type is {@code boolean}.
73   * Default value is {@code true}.
74   * </li>
75   * <li>
76   * Property {@code checkMethods} - Control whether to check references to methods.
77   * Type is {@code boolean}.
78   * Default value is {@code true}.
79   * </li>
80   * <li>
81   * Property {@code validateOnlyOverlapping} - Control whether to check only
82   * overlapping by variables or arguments.
83   * Type is {@code boolean}.
84   * Default value is {@code true}.
85   * </li>
86   * </ul>
87   *
88   * <p>
89   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
90   * </p>
91   *
92   * <p>
93   * Violation Message Keys:
94   * </p>
95   * <ul>
96   * <li>
97   * {@code require.this.method}
98   * </li>
99   * <li>
100  * {@code require.this.variable}
101  * </li>
102  * </ul>
103  *
104  * @since 3.4
105  */
106 // -@cs[ClassDataAbstractionCoupling] This check requires to work with and identify many frames.
107 @FileStatefulCheck
108 public class RequireThisCheck extends AbstractCheck {
109 
110     /**
111      * A key is pointing to the warning message text in "messages.properties"
112      * file.
113      */
114     public static final String MSG_METHOD = "require.this.method";
115     /**
116      * A key is pointing to the warning message text in "messages.properties"
117      * file.
118      */
119     public static final String MSG_VARIABLE = "require.this.variable";
120 
121     /** Set of all declaration tokens. */
122     private static final BitSet DECLARATION_TOKENS = TokenUtil.asBitSet(
123         TokenTypes.VARIABLE_DEF,
124         TokenTypes.CTOR_DEF,
125         TokenTypes.METHOD_DEF,
126         TokenTypes.CLASS_DEF,
127         TokenTypes.ENUM_DEF,
128         TokenTypes.ANNOTATION_DEF,
129         TokenTypes.INTERFACE_DEF,
130         TokenTypes.PARAMETER_DEF,
131         TokenTypes.TYPE_ARGUMENT,
132         TokenTypes.RECORD_DEF,
133         TokenTypes.RECORD_COMPONENT_DEF,
134         TokenTypes.RESOURCE
135     );
136     /** Set of all assign tokens. */
137     private static final BitSet ASSIGN_TOKENS = TokenUtil.asBitSet(
138         TokenTypes.ASSIGN,
139         TokenTypes.PLUS_ASSIGN,
140         TokenTypes.STAR_ASSIGN,
141         TokenTypes.DIV_ASSIGN,
142         TokenTypes.MOD_ASSIGN,
143         TokenTypes.SR_ASSIGN,
144         TokenTypes.BSR_ASSIGN,
145         TokenTypes.SL_ASSIGN,
146         TokenTypes.BAND_ASSIGN,
147         TokenTypes.BXOR_ASSIGN
148     );
149     /** Set of all compound assign tokens. */
150     private static final BitSet COMPOUND_ASSIGN_TOKENS = TokenUtil.asBitSet(
151         TokenTypes.PLUS_ASSIGN,
152         TokenTypes.STAR_ASSIGN,
153         TokenTypes.DIV_ASSIGN,
154         TokenTypes.MOD_ASSIGN,
155         TokenTypes.SR_ASSIGN,
156         TokenTypes.BSR_ASSIGN,
157         TokenTypes.SL_ASSIGN,
158         TokenTypes.BAND_ASSIGN,
159         TokenTypes.BXOR_ASSIGN
160     );
161 
162     /** Frame for the currently processed AST. */
163     private final Deque<AbstractFrame> current = new ArrayDeque<>();
164 
165     /** Tree of all the parsed frames. */
166     private Map<DetailAST, AbstractFrame> frames;
167 
168     /** Control whether to check references to fields. */
169     private boolean checkFields = true;
170     /** Control whether to check references to methods. */
171     private boolean checkMethods = true;
172     /** Control whether to check only overlapping by variables or arguments. */
173     private boolean validateOnlyOverlapping = true;
174 
175     /**
176      * Setter to control whether to check references to fields.
177      *
178      * @param checkFields should we check fields usage or not
179      * @since 3.4
180      */
181     public void setCheckFields(boolean checkFields) {
182         this.checkFields = checkFields;
183     }
184 
185     /**
186      * Setter to control whether to check references to methods.
187      *
188      * @param checkMethods should we check methods usage or not
189      * @since 3.4
190      */
191     public void setCheckMethods(boolean checkMethods) {
192         this.checkMethods = checkMethods;
193     }
194 
195     /**
196      * Setter to control whether to check only overlapping by variables or arguments.
197      *
198      * @param validateOnlyOverlapping should we check only overlapping by variables or arguments
199      * @since 6.17
200      */
201     public void setValidateOnlyOverlapping(boolean validateOnlyOverlapping) {
202         this.validateOnlyOverlapping = validateOnlyOverlapping;
203     }
204 
205     @Override
206     public int[] getDefaultTokens() {
207         return getRequiredTokens();
208     }
209 
210     @Override
211     public int[] getRequiredTokens() {
212         return new int[] {
213             TokenTypes.CLASS_DEF,
214             TokenTypes.INTERFACE_DEF,
215             TokenTypes.ENUM_DEF,
216             TokenTypes.ANNOTATION_DEF,
217             TokenTypes.CTOR_DEF,
218             TokenTypes.METHOD_DEF,
219             TokenTypes.LITERAL_FOR,
220             TokenTypes.SLIST,
221             TokenTypes.IDENT,
222             TokenTypes.RECORD_DEF,
223             TokenTypes.COMPACT_CTOR_DEF,
224             TokenTypes.LITERAL_TRY,
225             TokenTypes.RESOURCE,
226         };
227     }
228 
229     @Override
230     public int[] getAcceptableTokens() {
231         return getRequiredTokens();
232     }
233 
234     @Override
235     public void beginTree(DetailAST rootAST) {
236         frames = new HashMap<>();
237         current.clear();
238 
239         final Deque<AbstractFrame> frameStack = new LinkedList<>();
240         DetailAST curNode = rootAST;
241         while (curNode != null) {
242             collectDeclarations(frameStack, curNode);
243             DetailAST toVisit = curNode.getFirstChild();
244             while (curNode != null && toVisit == null) {
245                 endCollectingDeclarations(frameStack, curNode);
246                 toVisit = curNode.getNextSibling();
247                 curNode = curNode.getParent();
248             }
249             curNode = toVisit;
250         }
251     }
252 
253     @Override
254     public void visitToken(DetailAST ast) {
255         switch (ast.getType()) {
256             case TokenTypes.IDENT:
257                 processIdent(ast);
258                 break;
259             case TokenTypes.CLASS_DEF:
260             case TokenTypes.INTERFACE_DEF:
261             case TokenTypes.ENUM_DEF:
262             case TokenTypes.ANNOTATION_DEF:
263             case TokenTypes.SLIST:
264             case TokenTypes.METHOD_DEF:
265             case TokenTypes.CTOR_DEF:
266             case TokenTypes.LITERAL_FOR:
267             case TokenTypes.RECORD_DEF:
268                 current.push(frames.get(ast));
269                 break;
270             case TokenTypes.LITERAL_TRY:
271                 if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) {
272                     current.push(frames.get(ast));
273                 }
274                 break;
275             default:
276                 break;
277         }
278     }
279 
280     @Override
281     public void leaveToken(DetailAST ast) {
282         switch (ast.getType()) {
283             case TokenTypes.CLASS_DEF:
284             case TokenTypes.INTERFACE_DEF:
285             case TokenTypes.ENUM_DEF:
286             case TokenTypes.ANNOTATION_DEF:
287             case TokenTypes.SLIST:
288             case TokenTypes.METHOD_DEF:
289             case TokenTypes.CTOR_DEF:
290             case TokenTypes.LITERAL_FOR:
291             case TokenTypes.RECORD_DEF:
292                 current.pop();
293                 break;
294             case TokenTypes.LITERAL_TRY:
295                 if (current.peek().getType() == FrameType.TRY_WITH_RESOURCES_FRAME) {
296                     current.pop();
297                 }
298                 break;
299             default:
300                 break;
301         }
302     }
303 
304     /**
305      * Checks if a given IDENT is method call or field name which
306      * requires explicit {@code this} qualifier.
307      *
308      * @param ast IDENT to check.
309      */
310     private void processIdent(DetailAST ast) {
311         int parentType = ast.getParent().getType();
312         if (parentType == TokenTypes.EXPR
313                 && ast.getParent().getParent().getParent().getType()
314                     == TokenTypes.ANNOTATION_FIELD_DEF) {
315             parentType = TokenTypes.ANNOTATION_FIELD_DEF;
316         }
317         switch (parentType) {
318             case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR:
319             case TokenTypes.ANNOTATION:
320             case TokenTypes.ANNOTATION_FIELD_DEF:
321                 // no need to check annotations content
322                 break;
323             case TokenTypes.METHOD_CALL:
324                 if (checkMethods) {
325                     final AbstractFrame frame = getMethodWithoutThis(ast);
326                     if (frame != null) {
327                         logViolation(MSG_METHOD, ast, frame);
328                     }
329                 }
330                 break;
331             default:
332                 if (checkFields) {
333                     final AbstractFrame frame = getFieldWithoutThis(ast, parentType);
334                     final boolean canUseThis = !isInCompactConstructor(ast);
335                     if (frame != null && canUseThis) {
336                         logViolation(MSG_VARIABLE, ast, frame);
337                     }
338                 }
339                 break;
340         }
341     }
342 
343     /**
344      * Helper method to log a Violation.
345      *
346      * @param msgKey key to locale message format.
347      * @param ast a node to get line id column numbers associated with the message.
348      * @param frame the class frame where the violation is found.
349      */
350     private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) {
351         if (frame.getFrameName().equals(getNearestClassFrameName())) {
352             log(ast, msgKey, ast.getText(), "");
353         }
354         else if (!(frame instanceof AnonymousClassFrame)) {
355             log(ast, msgKey, ast.getText(), frame.getFrameName() + '.');
356         }
357     }
358 
359     /**
360      * Returns the frame where the field is declared, if the given field is used without
361      * 'this', and null otherwise.
362      *
363      * @param ast field definition ast token.
364      * @param parentType type of the parent.
365      * @return the frame where the field is declared, if the given field is used without
366      *         'this' and null otherwise.
367      */
368     private AbstractFrame getFieldWithoutThis(DetailAST ast, int parentType) {
369         final boolean importOrPackage = ScopeUtil.getSurroundingScope(ast) == null;
370         final boolean typeName = parentType == TokenTypes.TYPE
371                 || parentType == TokenTypes.LITERAL_NEW;
372         AbstractFrame frame = null;
373 
374         if (!importOrPackage
375                 && !typeName
376                 && !isDeclarationToken(parentType)
377                 && !isLambdaParameter(ast)) {
378             final AbstractFrame fieldFrame = findClassFrame(ast, false);
379 
380             if (fieldFrame != null && ((ClassFrame) fieldFrame).hasInstanceMember(ast)) {
381                 frame = getClassFrameWhereViolationIsFound(ast);
382             }
383         }
384         return frame;
385     }
386 
387     /**
388      * Return whether ast is in a COMPACT_CTOR_DEF.
389      *
390      * @param ast The token to check
391      * @return true if ast is in a COMPACT_CTOR_DEF, false otherwise
392      */
393     private static boolean isInCompactConstructor(DetailAST ast) {
394         boolean isInCompactCtor = false;
395         DetailAST parent = ast;
396         while (parent != null) {
397             if (parent.getType() == TokenTypes.COMPACT_CTOR_DEF) {
398                 isInCompactCtor = true;
399                 break;
400             }
401             parent = parent.getParent();
402         }
403         return isInCompactCtor;
404     }
405 
406     /**
407      * Parses the next AST for declarations.
408      *
409      * @param frameStack stack containing the FrameTree being built.
410      * @param ast AST to parse.
411      */
412     // -@cs[JavaNCSS] This method is a big switch and is too hard to remove.
413     private static void collectDeclarations(Deque<AbstractFrame> frameStack, DetailAST ast) {
414         final AbstractFrame frame = frameStack.peek();
415         switch (ast.getType()) {
416             case TokenTypes.VARIABLE_DEF:
417                 collectVariableDeclarations(ast, frame);
418                 break;
419             case TokenTypes.RECORD_COMPONENT_DEF:
420                 final DetailAST componentIdent = ast.findFirstToken(TokenTypes.IDENT);
421                 ((ClassFrame) frame).addInstanceMember(componentIdent);
422                 break;
423             case TokenTypes.PARAMETER_DEF:
424                 if (!CheckUtil.isReceiverParameter(ast)
425                         && !isLambdaParameter(ast)) {
426                     final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT);
427                     frame.addIdent(parameterIdent);
428                 }
429                 break;
430             case TokenTypes.RESOURCE:
431                 final DetailAST resourceIdent = ast.findFirstToken(TokenTypes.IDENT);
432                 if (resourceIdent != null) {
433                     frame.addIdent(resourceIdent);
434                 }
435                 break;
436             case TokenTypes.CLASS_DEF:
437             case TokenTypes.INTERFACE_DEF:
438             case TokenTypes.ENUM_DEF:
439             case TokenTypes.ANNOTATION_DEF:
440             case TokenTypes.RECORD_DEF:
441                 final DetailAST classFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
442                 frameStack.addFirst(new ClassFrame(frame, classFrameNameIdent));
443                 break;
444             case TokenTypes.SLIST:
445                 frameStack.addFirst(new BlockFrame(frame, ast));
446                 break;
447             case TokenTypes.METHOD_DEF:
448                 collectMethodDeclarations(frameStack, ast, frame);
449                 break;
450             case TokenTypes.CTOR_DEF:
451             case TokenTypes.COMPACT_CTOR_DEF:
452                 final DetailAST ctorFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
453                 frameStack.addFirst(new ConstructorFrame(frame, ctorFrameNameIdent));
454                 break;
455             case TokenTypes.ENUM_CONSTANT_DEF:
456                 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
457                 ((ClassFrame) frame).addStaticMember(ident);
458                 break;
459             case TokenTypes.LITERAL_CATCH:
460                 final AbstractFrame catchFrame = new CatchFrame(frame, ast);
461                 frameStack.addFirst(catchFrame);
462                 break;
463             case TokenTypes.LITERAL_FOR:
464                 final AbstractFrame forFrame = new ForFrame(frame, ast);
465                 frameStack.addFirst(forFrame);
466                 break;
467             case TokenTypes.LITERAL_NEW:
468                 if (isAnonymousClassDef(ast)) {
469                     frameStack.addFirst(new AnonymousClassFrame(frame,
470                             ast.toString()));
471                 }
472                 break;
473             case TokenTypes.LITERAL_TRY:
474                 if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) {
475                     frameStack.addFirst(new TryWithResourcesFrame(frame, ast));
476                 }
477                 break;
478             default:
479                 // do nothing
480         }
481     }
482 
483     /**
484      * Collects variable declarations.
485      *
486      * @param ast variable token.
487      * @param frame current frame.
488      */
489     private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) {
490         final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
491         if (frame.getType() == FrameType.CLASS_FRAME) {
492             final DetailAST mods =
493                     ast.findFirstToken(TokenTypes.MODIFIERS);
494             if (ScopeUtil.isInInterfaceBlock(ast)
495                     || mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
496                 ((ClassFrame) frame).addStaticMember(ident);
497             }
498             else {
499                 ((ClassFrame) frame).addInstanceMember(ident);
500             }
501         }
502         else {
503             frame.addIdent(ident);
504         }
505     }
506 
507     /**
508      * Collects {@code METHOD_DEF} declarations.
509      *
510      * @param frameStack stack containing the FrameTree being built.
511      * @param ast AST to parse.
512      * @param frame current frame.
513      */
514     private static void collectMethodDeclarations(Deque<AbstractFrame> frameStack,
515                                                   DetailAST ast, AbstractFrame frame) {
516         final DetailAST methodFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
517         final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
518         if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
519             ((ClassFrame) frame).addInstanceMethod(methodFrameNameIdent);
520         }
521         else {
522             ((ClassFrame) frame).addStaticMethod(methodFrameNameIdent);
523         }
524         frameStack.addFirst(new MethodFrame(frame, methodFrameNameIdent));
525     }
526 
527     /**
528      * Ends parsing of the AST for declarations.
529      *
530      * @param frameStack Stack containing the FrameTree being built.
531      * @param ast AST that was parsed.
532      */
533     private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, DetailAST ast) {
534         switch (ast.getType()) {
535             case TokenTypes.CLASS_DEF:
536             case TokenTypes.INTERFACE_DEF:
537             case TokenTypes.ENUM_DEF:
538             case TokenTypes.ANNOTATION_DEF:
539             case TokenTypes.SLIST:
540             case TokenTypes.METHOD_DEF:
541             case TokenTypes.CTOR_DEF:
542             case TokenTypes.LITERAL_CATCH:
543             case TokenTypes.LITERAL_FOR:
544             case TokenTypes.RECORD_DEF:
545             case TokenTypes.COMPACT_CTOR_DEF:
546                 frames.put(ast, frameStack.poll());
547                 break;
548             case TokenTypes.LITERAL_NEW:
549                 if (isAnonymousClassDef(ast)) {
550                     frameStack.remove();
551                 }
552                 break;
553             case TokenTypes.LITERAL_TRY:
554                 if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) {
555                     frames.put(ast, frameStack.poll());
556                 }
557                 break;
558             default:
559                 // do nothing
560         }
561     }
562 
563     /**
564      * Whether the AST is a definition of an anonymous class.
565      *
566      * @param ast the AST to process.
567      * @return true if the AST is a definition of an anonymous class.
568      */
569     private static boolean isAnonymousClassDef(DetailAST ast) {
570         final DetailAST lastChild = ast.getLastChild();
571         return lastChild != null
572             && lastChild.getType() == TokenTypes.OBJBLOCK;
573     }
574 
575     /**
576      * Returns the class frame where violation is found (where the field is used without 'this')
577      * or null otherwise.
578      *
579      * @param ast IDENT ast to check.
580      * @return the class frame where violation is found or null otherwise.
581      */
582     // -@cs[CyclomaticComplexity] Method already invokes too many methods that fully explain
583     // a logic, additional abstraction will not make logic/algorithm more readable.
584     private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) {
585         AbstractFrame frameWhereViolationIsFound = null;
586         final AbstractFrame variableDeclarationFrame = findFrame(ast, false);
587         final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType();
588         final DetailAST prevSibling = ast.getPreviousSibling();
589         if (variableDeclarationFrameType == FrameType.CLASS_FRAME
590                 && !validateOnlyOverlapping
591                 && (prevSibling == null || !isInExpression(ast))
592                 && canBeReferencedFromStaticContext(ast)) {
593             frameWhereViolationIsFound = variableDeclarationFrame;
594         }
595         else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) {
596             if (isOverlappingByArgument(ast)) {
597                 if (!isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
598                         && !isReturnedVariable(variableDeclarationFrame, ast)
599                         && canBeReferencedFromStaticContext(ast)
600                         && canAssignValueToClassField(ast)) {
601                     frameWhereViolationIsFound = findFrame(ast, true);
602                 }
603             }
604             else if (!validateOnlyOverlapping
605                      && prevSibling == null
606                      && isAssignToken(ast.getParent().getType())
607                      && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
608                      && canBeReferencedFromStaticContext(ast)
609                      && canAssignValueToClassField(ast)) {
610                 frameWhereViolationIsFound = findFrame(ast, true);
611             }
612         }
613         else if (variableDeclarationFrameType == FrameType.CTOR_FRAME
614                  && isOverlappingByArgument(ast)
615                  && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) {
616             frameWhereViolationIsFound = findFrame(ast, true);
617         }
618         else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME
619                     && isOverlappingByLocalVariable(ast)
620                     && canAssignValueToClassField(ast)
621                     && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
622                     && !isReturnedVariable(variableDeclarationFrame, ast)
623                     && canBeReferencedFromStaticContext(ast)) {
624             frameWhereViolationIsFound = findFrame(ast, true);
625         }
626         return frameWhereViolationIsFound;
627     }
628 
629     /**
630      * Checks ast parent is in expression.
631      *
632      * @param ast token to check
633      * @return true if token is part of expression, false otherwise
634      */
635     private static boolean isInExpression(DetailAST ast) {
636         return TokenTypes.DOT == ast.getParent().getType()
637                 || TokenTypes.METHOD_REF == ast.getParent().getType();
638     }
639 
640     /**
641      * Checks whether user arranges 'this' for variable in method, constructor, or block on his own.
642      *
643      * @param currentFrame current frame.
644      * @param ident ident token.
645      * @return true if user arranges 'this' for variable in method, constructor,
646      *         or block on his own.
647      */
648     private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame,
649                                                           DetailAST ident) {
650         final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
651         final DetailAST definitionToken = blockFrameNameIdent.getParent();
652         final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
653         final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
654 
655         boolean userDefinedArrangementOfThis = false;
656 
657         final Set<DetailAST> variableUsagesInsideBlock =
658             getAllTokensWhichAreEqualToCurrent(definitionToken, ident,
659                 blockEndToken.getLineNo());
660 
661         for (DetailAST variableUsage : variableUsagesInsideBlock) {
662             final DetailAST prevSibling = variableUsage.getPreviousSibling();
663             if (prevSibling != null
664                     && prevSibling.getType() == TokenTypes.LITERAL_THIS) {
665                 userDefinedArrangementOfThis = true;
666                 break;
667             }
668         }
669         return userDefinedArrangementOfThis;
670     }
671 
672     /**
673      * Returns the token which ends the code block.
674      *
675      * @param blockNameIdent block name identifier.
676      * @param blockStartToken token which starts the block.
677      * @return the token which ends the code block.
678      */
679     private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) {
680         DetailAST blockEndToken = null;
681         final DetailAST blockNameIdentParent = blockNameIdent.getParent();
682         if (blockNameIdentParent.getType() == TokenTypes.CASE_GROUP) {
683             blockEndToken = blockNameIdentParent.getNextSibling();
684         }
685         else {
686             final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent,
687                     TokenTypes.RCURLY);
688             for (DetailAST currentRcurly : rcurlyTokens) {
689                 final DetailAST parent = currentRcurly.getParent();
690                 if (TokenUtil.areOnSameLine(blockStartToken, parent)) {
691                     blockEndToken = currentRcurly;
692                 }
693             }
694         }
695         return blockEndToken;
696     }
697 
698     /**
699      * Checks whether the current variable is returned from the method.
700      *
701      * @param currentFrame current frame.
702      * @param ident variable ident token.
703      * @return true if the current variable is returned from the method.
704      */
705     private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) {
706         final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
707         final DetailAST definitionToken = blockFrameNameIdent.getParent();
708         final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
709         final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
710 
711         final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken,
712             TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo());
713 
714         return returnsInsideBlock.stream()
715             .anyMatch(returnToken -> isAstInside(returnToken, ident));
716     }
717 
718     /**
719      * Checks if the given {@code ast} is equal to the {@code tree} or a child of it.
720      *
721      * @param tree The tree to search.
722      * @param ast The AST to look for.
723      * @return {@code true} if the {@code ast} was found.
724      */
725     private static boolean isAstInside(DetailAST tree, DetailAST ast) {
726         boolean result = false;
727 
728         if (isAstSimilar(tree, ast)) {
729             result = true;
730         }
731         else {
732             for (DetailAST child = tree.getFirstChild(); child != null
733                     && !result; child = child.getNextSibling()) {
734                 result = isAstInside(child, ast);
735             }
736         }
737 
738         return result;
739     }
740 
741     /**
742      * Checks whether a field can be referenced from a static context.
743      *
744      * @param ident ident token.
745      * @return true if field can be referenced from a static context.
746      */
747     private static boolean canBeReferencedFromStaticContext(DetailAST ident) {
748         boolean staticContext = false;
749 
750         final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident);
751         if (codeBlockDefinition != null) {
752             final DetailAST modifiers = codeBlockDefinition.getFirstChild();
753             staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT
754                 || modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
755         }
756         return !staticContext;
757     }
758 
759     /**
760      * Returns code block definition token for current identifier.
761      *
762      * @param ident ident token.
763      * @return code block definition token for current identifier or null if code block
764      *         definition was not found.
765      */
766     private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) {
767         DetailAST parent = ident;
768         while (parent != null
769                && parent.getType() != TokenTypes.METHOD_DEF
770                && parent.getType() != TokenTypes.STATIC_INIT) {
771             parent = parent.getParent();
772         }
773         return parent;
774     }
775 
776     /**
777      * Checks whether a value can be assigned to a field.
778      * A value can be assigned to a final field only in constructor block. If there is a method
779      * block, value assignment can be performed only to non final field.
780      *
781      * @param ast an identifier token.
782      * @return true if a value can be assigned to a field.
783      */
784     private boolean canAssignValueToClassField(DetailAST ast) {
785         final AbstractFrame fieldUsageFrame = findFrame(ast, false);
786         final boolean fieldUsageInConstructor = isInsideConstructorFrame(fieldUsageFrame);
787 
788         final AbstractFrame declarationFrame = findFrame(ast, true);
789         final boolean finalField = ((ClassFrame) declarationFrame).hasFinalField(ast);
790 
791         return fieldUsageInConstructor || !finalField;
792     }
793 
794     /**
795      * Checks whether a field usage frame is inside constructor frame.
796      *
797      * @param frame frame, where field is used.
798      * @return true if the field usage frame is inside constructor frame.
799      */
800     private static boolean isInsideConstructorFrame(AbstractFrame frame) {
801         AbstractFrame fieldUsageFrame = frame;
802         while (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
803             fieldUsageFrame = fieldUsageFrame.getParent();
804         }
805         return fieldUsageFrame.getType() == FrameType.CTOR_FRAME;
806     }
807 
808     /**
809      * Checks whether an overlapping by method or constructor argument takes place.
810      *
811      * @param ast an identifier.
812      * @return true if an overlapping by method or constructor argument takes place.
813      */
814     private boolean isOverlappingByArgument(DetailAST ast) {
815         boolean overlapping = false;
816         final DetailAST parent = ast.getParent();
817         final DetailAST sibling = ast.getNextSibling();
818         if (sibling != null && isAssignToken(parent.getType())) {
819             if (isCompoundAssignToken(parent.getType())) {
820                 overlapping = true;
821             }
822             else {
823                 final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
824                 final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
825                 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
826             }
827         }
828         return overlapping;
829     }
830 
831     /**
832      * Checks whether an overlapping by local variable takes place.
833      *
834      * @param ast an identifier.
835      * @return true if an overlapping by local variable takes place.
836      */
837     private boolean isOverlappingByLocalVariable(DetailAST ast) {
838         boolean overlapping = false;
839         final DetailAST parent = ast.getParent();
840         if (isAssignToken(parent.getType())) {
841             final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
842             final Set<DetailAST> exprIdents =
843                 getAllTokensOfType(ast.getNextSibling(), TokenTypes.IDENT);
844             overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
845         }
846         return overlapping;
847     }
848 
849     /**
850      * Collects all tokens of specific type starting with the current ast node.
851      *
852      * @param ast ast node.
853      * @param tokenType token type.
854      * @return a set of all tokens of specific type starting with the current ast node.
855      */
856     private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
857         DetailAST vertex = ast;
858         final Set<DetailAST> result = new HashSet<>();
859         final Deque<DetailAST> stack = new ArrayDeque<>();
860         while (vertex != null || !stack.isEmpty()) {
861             if (!stack.isEmpty()) {
862                 vertex = stack.pop();
863             }
864             while (vertex != null) {
865                 if (vertex.getType() == tokenType) {
866                     result.add(vertex);
867                 }
868                 if (vertex.getNextSibling() != null) {
869                     stack.push(vertex.getNextSibling());
870                 }
871                 vertex = vertex.getFirstChild();
872             }
873         }
874         return result;
875     }
876 
877     /**
878      * Collects all tokens of specific type starting with the current ast node and which line
879      * number is lower or equal to the end line number.
880      *
881      * @param ast ast node.
882      * @param tokenType token type.
883      * @param endLineNumber end line number.
884      * @return a set of all tokens of specific type starting with the current ast node and which
885      *         line number is lower or equal to the end line number.
886      */
887     private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType,
888                                                      int endLineNumber) {
889         DetailAST vertex = ast;
890         final Set<DetailAST> result = new HashSet<>();
891         final Deque<DetailAST> stack = new ArrayDeque<>();
892         while (vertex != null || !stack.isEmpty()) {
893             if (!stack.isEmpty()) {
894                 vertex = stack.pop();
895             }
896             while (vertex != null) {
897                 if (tokenType == vertex.getType()
898                     && vertex.getLineNo() <= endLineNumber) {
899                     result.add(vertex);
900                 }
901                 if (vertex.getNextSibling() != null) {
902                     stack.push(vertex.getNextSibling());
903                 }
904                 vertex = vertex.getFirstChild();
905             }
906         }
907         return result;
908     }
909 
910     /**
911      * Collects all tokens which are equal to current token starting with the current ast node and
912      * which line number is lower or equal to the end line number.
913      *
914      * @param ast ast node.
915      * @param token token.
916      * @param endLineNumber end line number.
917      * @return a set of tokens which are equal to current token starting with the current ast node
918      *         and which line number is lower or equal to the end line number.
919      */
920     private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token,
921                                                                      int endLineNumber) {
922         DetailAST vertex = ast;
923         final Set<DetailAST> result = new HashSet<>();
924         final Deque<DetailAST> stack = new ArrayDeque<>();
925         while (vertex != null || !stack.isEmpty()) {
926             if (!stack.isEmpty()) {
927                 vertex = stack.pop();
928             }
929             while (vertex != null) {
930                 if (isAstSimilar(token, vertex)
931                         && vertex.getLineNo() <= endLineNumber) {
932                     result.add(vertex);
933                 }
934                 if (vertex.getNextSibling() != null) {
935                     stack.push(vertex.getNextSibling());
936                 }
937                 vertex = vertex.getFirstChild();
938             }
939         }
940         return result;
941     }
942 
943     /**
944      * Returns the frame where the method is declared, if the given method is used without
945      * 'this' and null otherwise.
946      *
947      * @param ast the IDENT ast of the name to check.
948      * @return the frame where the method is declared, if the given method is used without
949      *         'this' and null otherwise.
950      */
951     private AbstractFrame getMethodWithoutThis(DetailAST ast) {
952         AbstractFrame result = null;
953         if (!validateOnlyOverlapping) {
954             final AbstractFrame frame = findFrame(ast, true);
955             if (frame != null
956                     && ((ClassFrame) frame).hasInstanceMethod(ast)
957                     && !((ClassFrame) frame).hasStaticMethod(ast)) {
958                 result = frame;
959             }
960         }
961         return result;
962     }
963 
964     /**
965      * Find the class frame containing declaration.
966      *
967      * @param name IDENT ast of the declaration to find.
968      * @param lookForMethod whether we are looking for a method name.
969      * @return AbstractFrame containing declaration or null.
970      */
971     private AbstractFrame findClassFrame(DetailAST name, boolean lookForMethod) {
972         AbstractFrame frame = current.peek();
973 
974         while (true) {
975             frame = findFrame(frame, name, lookForMethod);
976 
977             if (frame == null || frame instanceof ClassFrame) {
978                 break;
979             }
980 
981             frame = frame.getParent();
982         }
983 
984         return frame;
985     }
986 
987     /**
988      * Find frame containing declaration.
989      *
990      * @param name IDENT ast of the declaration to find.
991      * @param lookForMethod whether we are looking for a method name.
992      * @return AbstractFrame containing declaration or null.
993      */
994     private AbstractFrame findFrame(DetailAST name, boolean lookForMethod) {
995         return findFrame(current.peek(), name, lookForMethod);
996     }
997 
998     /**
999      * Find frame containing declaration.
1000      *
1001      * @param frame The parent frame to searching in.
1002      * @param name IDENT ast of the declaration to find.
1003      * @param lookForMethod whether we are looking for a method name.
1004      * @return AbstractFrame containing declaration or null.
1005      */
1006     private static AbstractFrame findFrame(AbstractFrame frame, DetailAST name,
1007             boolean lookForMethod) {
1008         return frame.getIfContains(name, lookForMethod);
1009     }
1010 
1011     /**
1012      * Check that token is related to Definition tokens.
1013      *
1014      * @param parentType token Type.
1015      * @return true if token is related to Definition Tokens.
1016      */
1017     private static boolean isDeclarationToken(int parentType) {
1018         return DECLARATION_TOKENS.get(parentType);
1019     }
1020 
1021     /**
1022      * Check that token is related to assign tokens.
1023      *
1024      * @param tokenType token type.
1025      * @return true if token is related to assign tokens.
1026      */
1027     private static boolean isAssignToken(int tokenType) {
1028         return ASSIGN_TOKENS.get(tokenType);
1029     }
1030 
1031     /**
1032      * Check that token is related to compound assign tokens.
1033      *
1034      * @param tokenType token type.
1035      * @return true if token is related to compound assign tokens.
1036      */
1037     private static boolean isCompoundAssignToken(int tokenType) {
1038         return COMPOUND_ASSIGN_TOKENS.get(tokenType);
1039     }
1040 
1041     /**
1042      * Gets the name of the nearest parent ClassFrame.
1043      *
1044      * @return the name of the nearest parent ClassFrame.
1045      */
1046     private String getNearestClassFrameName() {
1047         AbstractFrame frame = current.peek();
1048         while (frame.getType() != FrameType.CLASS_FRAME) {
1049             frame = frame.getParent();
1050         }
1051         return frame.getFrameName();
1052     }
1053 
1054     /**
1055      * Checks if the token is a Lambda parameter.
1056      *
1057      * @param ast the {@code DetailAST} value of the token to be checked
1058      * @return true if the token is a Lambda parameter
1059      */
1060     private static boolean isLambdaParameter(DetailAST ast) {
1061         DetailAST parent;
1062         for (parent = ast; parent != null; parent = parent.getParent()) {
1063             if (parent.getType() == TokenTypes.LAMBDA) {
1064                 break;
1065             }
1066         }
1067         final boolean isLambdaParameter;
1068         if (parent == null) {
1069             isLambdaParameter = false;
1070         }
1071         else if (ast.getType() == TokenTypes.PARAMETER_DEF) {
1072             isLambdaParameter = true;
1073         }
1074         else {
1075             final DetailAST lambdaParameters = parent.findFirstToken(TokenTypes.PARAMETERS);
1076             if (lambdaParameters == null) {
1077                 isLambdaParameter = parent.getFirstChild().getText().equals(ast.getText());
1078             }
1079             else {
1080                 isLambdaParameter = TokenUtil.findFirstTokenByPredicate(lambdaParameters,
1081                     paramDef -> {
1082                         final DetailAST param = paramDef.findFirstToken(TokenTypes.IDENT);
1083                         return param != null && param.getText().equals(ast.getText());
1084                     }).isPresent();
1085             }
1086         }
1087         return isLambdaParameter;
1088     }
1089 
1090     /**
1091      * Checks if 2 AST are similar by their type and text.
1092      *
1093      * @param left The first AST to check.
1094      * @param right The second AST to check.
1095      * @return {@code true} if they are similar.
1096      */
1097     private static boolean isAstSimilar(DetailAST left, DetailAST right) {
1098         return left.getType() == right.getType() && left.getText().equals(right.getText());
1099     }
1100 
1101     /** An AbstractFrame type. */
1102     private enum FrameType {
1103 
1104         /** Class frame type. */
1105         CLASS_FRAME,
1106         /** Constructor frame type. */
1107         CTOR_FRAME,
1108         /** Method frame type. */
1109         METHOD_FRAME,
1110         /** Block frame type. */
1111         BLOCK_FRAME,
1112         /** Catch frame type. */
1113         CATCH_FRAME,
1114         /** For frame type. */
1115         FOR_FRAME,
1116         /** Try with resources frame type. */
1117         TRY_WITH_RESOURCES_FRAME
1118 
1119     }
1120 
1121     /**
1122      * A declaration frame.
1123      */
1124     private abstract static class AbstractFrame {
1125 
1126         /** Set of name of variables declared in this frame. */
1127         private final Set<DetailAST> varIdents;
1128 
1129         /** Parent frame. */
1130         private final AbstractFrame parent;
1131 
1132         /** Name identifier token. */
1133         private final DetailAST frameNameIdent;
1134 
1135         /**
1136          * Constructor -- invocable only via super() from subclasses.
1137          *
1138          * @param parent parent frame.
1139          * @param ident frame name ident.
1140          */
1141         protected AbstractFrame(AbstractFrame parent, DetailAST ident) {
1142             this.parent = parent;
1143             frameNameIdent = ident;
1144             varIdents = new HashSet<>();
1145         }
1146 
1147         /**
1148          * Get the type of the frame.
1149          *
1150          * @return a FrameType.
1151          */
1152         protected abstract FrameType getType();
1153 
1154         /**
1155          * Add a name to the frame.
1156          *
1157          * @param identToAdd the name we're adding.
1158          */
1159         private void addIdent(DetailAST identToAdd) {
1160             varIdents.add(identToAdd);
1161         }
1162 
1163         /**
1164          * Returns the parent frame.
1165          *
1166          * @return the parent frame
1167          */
1168         protected AbstractFrame getParent() {
1169             return parent;
1170         }
1171 
1172         /**
1173          * Returns the name identifier text.
1174          *
1175          * @return the name identifier text
1176          */
1177         protected String getFrameName() {
1178             return frameNameIdent.getText();
1179         }
1180 
1181         /**
1182          * Returns the name identifier token.
1183          *
1184          * @return the name identifier token
1185          */
1186         public DetailAST getFrameNameIdent() {
1187             return frameNameIdent;
1188         }
1189 
1190         /**
1191          * Check whether the frame contains a field or a variable with the given name.
1192          *
1193          * @param identToFind the IDENT ast of the name we're looking for.
1194          * @return whether it was found.
1195          */
1196         protected boolean containsFieldOrVariable(DetailAST identToFind) {
1197             return containsFieldOrVariableDef(varIdents, identToFind);
1198         }
1199 
1200         /**
1201          * Check whether the frame contains a given name.
1202          *
1203          * @param identToFind IDENT ast of the name we're looking for.
1204          * @param lookForMethod whether we are looking for a method name.
1205          * @return whether it was found.
1206          */
1207         protected AbstractFrame getIfContains(DetailAST identToFind, boolean lookForMethod) {
1208             final AbstractFrame frame;
1209 
1210             if (!lookForMethod
1211                 && containsFieldOrVariable(identToFind)) {
1212                 frame = this;
1213             }
1214             else {
1215                 frame = parent.getIfContains(identToFind, lookForMethod);
1216             }
1217             return frame;
1218         }
1219 
1220         /**
1221          * Whether the set contains a declaration with the text of the specified
1222          * IDENT ast and it is declared in a proper position.
1223          *
1224          * @param set the set of declarations.
1225          * @param ident the specified IDENT ast.
1226          * @return true if the set contains a declaration with the text of the specified
1227          *         IDENT ast and it is declared in a proper position.
1228          */
1229         protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) {
1230             boolean result = false;
1231             for (DetailAST ast: set) {
1232                 if (isProperDefinition(ident, ast)) {
1233                     result = true;
1234                     break;
1235                 }
1236             }
1237             return result;
1238         }
1239 
1240         /**
1241          * Whether the definition is correspondent to the IDENT.
1242          *
1243          * @param ident the IDENT ast to check.
1244          * @param ast the IDENT ast of the definition to check.
1245          * @return true if ast is correspondent to ident.
1246          */
1247         protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1248             final String identToFind = ident.getText();
1249             return identToFind.equals(ast.getText())
1250                 && CheckUtil.isBeforeInSource(ast, ident);
1251         }
1252     }
1253 
1254     /**
1255      * A frame initiated at method definition; holds a method definition token.
1256      */
1257     private static class MethodFrame extends AbstractFrame {
1258 
1259         /**
1260          * Creates method frame.
1261          *
1262          * @param parent parent frame.
1263          * @param ident method name identifier token.
1264          */
1265         protected MethodFrame(AbstractFrame parent, DetailAST ident) {
1266             super(parent, ident);
1267         }
1268 
1269         @Override
1270         protected FrameType getType() {
1271             return FrameType.METHOD_FRAME;
1272         }
1273 
1274     }
1275 
1276     /**
1277      * A frame initiated at constructor definition.
1278      */
1279     private static class ConstructorFrame extends AbstractFrame {
1280 
1281         /**
1282          * Creates a constructor frame.
1283          *
1284          * @param parent parent frame.
1285          * @param ident frame name ident.
1286          */
1287         protected ConstructorFrame(AbstractFrame parent, DetailAST ident) {
1288             super(parent, ident);
1289         }
1290 
1291         @Override
1292         protected FrameType getType() {
1293             return FrameType.CTOR_FRAME;
1294         }
1295 
1296     }
1297 
1298     /**
1299      * A frame initiated at class, enum or interface definition; holds instance variable names.
1300      */
1301     private static class ClassFrame extends AbstractFrame {
1302 
1303         /** Set of idents of instance members declared in this frame. */
1304         private final Set<DetailAST> instanceMembers;
1305         /** Set of idents of instance methods declared in this frame. */
1306         private final Set<DetailAST> instanceMethods;
1307         /** Set of idents of variables declared in this frame. */
1308         private final Set<DetailAST> staticMembers;
1309         /** Set of idents of static methods declared in this frame. */
1310         private final Set<DetailAST> staticMethods;
1311 
1312         /**
1313          * Creates new instance of ClassFrame.
1314          *
1315          * @param parent parent frame.
1316          * @param ident frame name ident.
1317          */
1318         private ClassFrame(AbstractFrame parent, DetailAST ident) {
1319             super(parent, ident);
1320             instanceMembers = new HashSet<>();
1321             instanceMethods = new HashSet<>();
1322             staticMembers = new HashSet<>();
1323             staticMethods = new HashSet<>();
1324         }
1325 
1326         @Override
1327         protected FrameType getType() {
1328             return FrameType.CLASS_FRAME;
1329         }
1330 
1331         /**
1332          * Adds static member's ident.
1333          *
1334          * @param ident an ident of static member of the class.
1335          */
1336         public void addStaticMember(final DetailAST ident) {
1337             staticMembers.add(ident);
1338         }
1339 
1340         /**
1341          * Adds static method's name.
1342          *
1343          * @param ident an ident of static method of the class.
1344          */
1345         public void addStaticMethod(final DetailAST ident) {
1346             staticMethods.add(ident);
1347         }
1348 
1349         /**
1350          * Adds instance member's ident.
1351          *
1352          * @param ident an ident of instance member of the class.
1353          */
1354         public void addInstanceMember(final DetailAST ident) {
1355             instanceMembers.add(ident);
1356         }
1357 
1358         /**
1359          * Adds instance method's name.
1360          *
1361          * @param ident an ident of instance method of the class.
1362          */
1363         public void addInstanceMethod(final DetailAST ident) {
1364             instanceMethods.add(ident);
1365         }
1366 
1367         /**
1368          * Checks if a given name is a known instance member of the class.
1369          *
1370          * @param ident the IDENT ast of the name to check.
1371          * @return true is the given name is a name of a known
1372          *         instance member of the class.
1373          */
1374         public boolean hasInstanceMember(final DetailAST ident) {
1375             return containsFieldOrVariableDef(instanceMembers, ident);
1376         }
1377 
1378         /**
1379          * Checks if a given name is a known instance method of the class.
1380          *
1381          * @param ident the IDENT ast of the method call to check.
1382          * @return true if the given ast is correspondent to a known
1383          *         instance method of the class.
1384          */
1385         public boolean hasInstanceMethod(final DetailAST ident) {
1386             return containsMethodDef(instanceMethods, ident);
1387         }
1388 
1389         /**
1390          * Checks if a given name is a known static method of the class.
1391          *
1392          * @param ident the IDENT ast of the method call to check.
1393          * @return true is the given ast is correspondent to a known
1394          *         instance method of the class.
1395          */
1396         public boolean hasStaticMethod(final DetailAST ident) {
1397             return containsMethodDef(staticMethods, ident);
1398         }
1399 
1400         /**
1401          * Checks whether given instance member has final modifier.
1402          *
1403          * @param instanceMember an instance member of a class.
1404          * @return true if given instance member has final modifier.
1405          */
1406         public boolean hasFinalField(final DetailAST instanceMember) {
1407             boolean result = false;
1408             for (DetailAST member : instanceMembers) {
1409                 final DetailAST parent = member.getParent();
1410                 if (parent.getType() == TokenTypes.RECORD_COMPONENT_DEF) {
1411                     result = true;
1412                 }
1413                 else {
1414                     final DetailAST mods = parent.findFirstToken(TokenTypes.MODIFIERS);
1415                     final boolean finalMod = mods.findFirstToken(TokenTypes.FINAL) != null;
1416                     if (finalMod && isAstSimilar(member, instanceMember)) {
1417                         result = true;
1418                     }
1419                 }
1420             }
1421             return result;
1422         }
1423 
1424         @Override
1425         protected boolean containsFieldOrVariable(DetailAST identToFind) {
1426             return containsFieldOrVariableDef(instanceMembers, identToFind)
1427                     || containsFieldOrVariableDef(staticMembers, identToFind);
1428         }
1429 
1430         @Override
1431         protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1432             final String identToFind = ident.getText();
1433             return identToFind.equals(ast.getText());
1434         }
1435 
1436         @Override
1437         protected AbstractFrame getIfContains(DetailAST identToFind, boolean lookForMethod) {
1438             AbstractFrame frame = null;
1439 
1440             if (containsMethod(identToFind)
1441                 || containsFieldOrVariable(identToFind)) {
1442                 frame = this;
1443             }
1444             else if (getParent() != null) {
1445                 frame = getParent().getIfContains(identToFind, lookForMethod);
1446             }
1447             return frame;
1448         }
1449 
1450         /**
1451          * Check whether the frame contains a given method.
1452          *
1453          * @param methodToFind the AST of the method to find.
1454          * @return true, if a method with the same name and number of parameters is found.
1455          */
1456         private boolean containsMethod(DetailAST methodToFind) {
1457             return containsMethodDef(instanceMethods, methodToFind)
1458                 || containsMethodDef(staticMethods, methodToFind);
1459         }
1460 
1461         /**
1462          * Whether the set contains a method definition with the
1463          *     same name and number of parameters.
1464          *
1465          * @param set the set of definitions.
1466          * @param ident the specified method call IDENT ast.
1467          * @return true if the set contains a definition with the
1468          *     same name and number of parameters.
1469          */
1470         private static boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) {
1471             boolean result = false;
1472             for (DetailAST ast: set) {
1473                 if (isSimilarSignature(ident, ast)) {
1474                     result = true;
1475                     break;
1476                 }
1477             }
1478             return result;
1479         }
1480 
1481         /**
1482          * Whether the method definition has the same name and number of parameters.
1483          *
1484          * @param ident the specified method call IDENT ast.
1485          * @param ast the ast of a method definition to compare with.
1486          * @return true if a method definition has the same name and number of parameters
1487          *     as the method call.
1488          */
1489         private static boolean isSimilarSignature(DetailAST ident, DetailAST ast) {
1490             boolean result = false;
1491             final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST);
1492             if (elistToken != null && ident.getText().equals(ast.getText())) {
1493                 final int paramsNumber =
1494                     ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount();
1495                 final int argsNumber = elistToken.getChildCount();
1496                 result = paramsNumber == argsNumber;
1497             }
1498             return result;
1499         }
1500 
1501     }
1502 
1503     /**
1504      * An anonymous class frame; holds instance variable names.
1505      */
1506     private static class AnonymousClassFrame extends ClassFrame {
1507 
1508         /** The name of the frame. */
1509         private final String frameName;
1510 
1511         /**
1512          * Creates anonymous class frame.
1513          *
1514          * @param parent parent frame.
1515          * @param frameName name of the frame.
1516          */
1517         protected AnonymousClassFrame(AbstractFrame parent, String frameName) {
1518             super(parent, null);
1519             this.frameName = frameName;
1520         }
1521 
1522         @Override
1523         protected String getFrameName() {
1524             return frameName;
1525         }
1526 
1527     }
1528 
1529     /**
1530      * A frame initiated on entering a statement list; holds local variable names.
1531      */
1532     private static class BlockFrame extends AbstractFrame {
1533 
1534         /**
1535          * Creates block frame.
1536          *
1537          * @param parent parent frame.
1538          * @param ident ident frame name ident.
1539          */
1540         protected BlockFrame(AbstractFrame parent, DetailAST ident) {
1541             super(parent, ident);
1542         }
1543 
1544         @Override
1545         protected FrameType getType() {
1546             return FrameType.BLOCK_FRAME;
1547         }
1548 
1549     }
1550 
1551     /**
1552      * A frame initiated on entering a catch block; holds local catch variable names.
1553      */
1554     private static class CatchFrame extends AbstractFrame {
1555 
1556         /**
1557          * Creates catch frame.
1558          *
1559          * @param parent parent frame.
1560          * @param ident ident frame name ident.
1561          */
1562         protected CatchFrame(AbstractFrame parent, DetailAST ident) {
1563             super(parent, ident);
1564         }
1565 
1566         @Override
1567         public FrameType getType() {
1568             return FrameType.CATCH_FRAME;
1569         }
1570 
1571         @Override
1572         protected AbstractFrame getIfContains(DetailAST identToFind, boolean lookForMethod) {
1573             final AbstractFrame frame;
1574 
1575             if (!lookForMethod
1576                     && containsFieldOrVariable(identToFind)) {
1577                 frame = this;
1578             }
1579             else if (getParent().getType() == FrameType.TRY_WITH_RESOURCES_FRAME) {
1580                 // Skip try-with-resources frame because resources cannot be accessed from catch
1581                 frame = getParent().getParent().getIfContains(identToFind, lookForMethod);
1582             }
1583             else {
1584                 frame = getParent().getIfContains(identToFind, lookForMethod);
1585             }
1586             return frame;
1587         }
1588 
1589     }
1590 
1591     /**
1592      * A frame initiated on entering a for block; holds local for variable names.
1593      */
1594     private static class ForFrame extends AbstractFrame {
1595 
1596         /**
1597          * Creates for frame.
1598          *
1599          * @param parent parent frame.
1600          * @param ident ident frame name ident.
1601          */
1602         protected ForFrame(AbstractFrame parent, DetailAST ident) {
1603             super(parent, ident);
1604         }
1605 
1606         @Override
1607         public FrameType getType() {
1608             return FrameType.FOR_FRAME;
1609         }
1610 
1611     }
1612 
1613     /**
1614      * A frame initiated on entering a try-with-resources construct;
1615      * holds local resources for the try block.
1616      */
1617     private static class TryWithResourcesFrame extends AbstractFrame {
1618 
1619         /**
1620          * Creates try-with-resources frame.
1621          *
1622          * @param parent parent frame.
1623          * @param ident ident frame name ident.
1624          */
1625         protected TryWithResourcesFrame(AbstractFrame parent, DetailAST ident) {
1626             super(parent, ident);
1627         }
1628 
1629         @Override
1630         public FrameType getType() {
1631             return FrameType.TRY_WITH_RESOURCES_FRAME;
1632         }
1633 
1634     }
1635 
1636 }