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