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