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