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