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