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