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