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