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