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