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