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