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