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