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