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