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