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