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