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