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