001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 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.Arrays;
024import java.util.Collections;
025import java.util.Deque;
026import java.util.HashMap;
027import java.util.HashSet;
028import java.util.LinkedList;
029import java.util.Map;
030import java.util.Queue;
031import java.util.Set;
032import java.util.stream.Collectors;
033
034import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
035import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
036import com.puppycrawl.tools.checkstyle.api.DetailAST;
037import com.puppycrawl.tools.checkstyle.api.TokenTypes;
038import com.puppycrawl.tools.checkstyle.utils.CheckUtils;
039import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
040import com.puppycrawl.tools.checkstyle.utils.TokenUtils;
041
042/**
043 * <p>Checks that code doesn't rely on the &quot;this&quot; default.
044 * That is references to instance variables and methods of the present
045 * object are explicitly of the form &quot;this.varName&quot; or
046 * &quot;this.methodName(args)&quot;.
047 * </p>
048 * Check has the following options:
049 * <p><b>checkFields</b> - whether to check references to fields. Default value is <b>true</b>.</p>
050 * <p><b>checkMethods</b> - whether to check references to methods.
051 * Default value is <b>true</b>.</p>
052 * <p><b>validateOnlyOverlapping</b> - whether to check only overlapping by variables or
053 * arguments. Default value is <b>true</b>.</p>
054 *
055 * <p>Warning: the Check is very controversial if 'validateOnlyOverlapping' option is set to 'false'
056 * and not that actual nowadays.</p>
057 *
058 * <p>Examples of use:
059 * <pre>
060 * &lt;module name=&quot;RequireThis&quot;/&gt;
061 * </pre>
062 * An example of how to configure to check {@code this} qualifier for
063 * methods only:
064 * <pre>
065 * &lt;module name=&quot;RequireThis&quot;&gt;
066 *   &lt;property name=&quot;checkFields&quot; value=&quot;false&quot;/&gt;
067 *   &lt;property name=&quot;checkMethods&quot; value=&quot;true&quot;/&gt;
068 * &lt;/module&gt;
069 * </pre>
070 *
071 * <p>Rationale:</p>
072 * <ol>
073 *   <li>
074 *     The same notation/habit for C++ and Java (C++ have global methods, so having
075 *     &quot;this.&quot; do make sense in it to distinguish call of method of class
076 *     instead of global).
077 *   </li>
078 *   <li>
079 *     Non-IDE development (ease of refactoring, some clearness to distinguish
080 *     static and non-static methods).
081 *   </li>
082 * </ol>
083 *
084 * <p>Limitations: Nothing is currently done about static variables
085 * or catch-blocks.  Static methods invoked on a class name seem to be OK;
086 * both the class name and the method name have a DOT parent.
087 * Non-static methods invoked on either this or a variable name seem to be
088 * OK, likewise.</p>
089 *
090 * @author Stephen Bloch
091 * @author o_sukhodolsky
092 * @author Andrei Selkin
093 */
094@FileStatefulCheck
095public class RequireThisCheck extends AbstractCheck {
096
097    /**
098     * A key is pointing to the warning message text in "messages.properties"
099     * file.
100     */
101    public static final String MSG_METHOD = "require.this.method";
102    /**
103     * A key is pointing to the warning message text in "messages.properties"
104     * file.
105     */
106    public static final String MSG_VARIABLE = "require.this.variable";
107
108    /** Set of all declaration tokens. */
109    private static final Set<Integer> DECLARATION_TOKENS = Collections.unmodifiableSet(
110        Arrays.stream(new Integer[] {
111            TokenTypes.VARIABLE_DEF,
112            TokenTypes.CTOR_DEF,
113            TokenTypes.METHOD_DEF,
114            TokenTypes.CLASS_DEF,
115            TokenTypes.ENUM_DEF,
116            TokenTypes.INTERFACE_DEF,
117            TokenTypes.PARAMETER_DEF,
118            TokenTypes.TYPE_ARGUMENT,
119        }).collect(Collectors.toSet()));
120    /** Set of all assign tokens. */
121    private static final Set<Integer> ASSIGN_TOKENS = Collections.unmodifiableSet(
122        Arrays.stream(new Integer[] {
123            TokenTypes.ASSIGN,
124            TokenTypes.PLUS_ASSIGN,
125            TokenTypes.STAR_ASSIGN,
126            TokenTypes.DIV_ASSIGN,
127            TokenTypes.MOD_ASSIGN,
128            TokenTypes.SR_ASSIGN,
129            TokenTypes.BSR_ASSIGN,
130            TokenTypes.SL_ASSIGN,
131            TokenTypes.BAND_ASSIGN,
132            TokenTypes.BXOR_ASSIGN,
133        }).collect(Collectors.toSet()));
134    /** Set of all compound assign tokens. */
135    private static final Set<Integer> COMPOUND_ASSIGN_TOKENS = Collections.unmodifiableSet(
136        Arrays.stream(new Integer[] {
137            TokenTypes.PLUS_ASSIGN,
138            TokenTypes.STAR_ASSIGN,
139            TokenTypes.DIV_ASSIGN,
140            TokenTypes.MOD_ASSIGN,
141            TokenTypes.SR_ASSIGN,
142            TokenTypes.BSR_ASSIGN,
143            TokenTypes.SL_ASSIGN,
144            TokenTypes.BAND_ASSIGN,
145            TokenTypes.BXOR_ASSIGN,
146        }).collect(Collectors.toSet()));
147
148    /** Tree of all the parsed frames. */
149    private Map<DetailAST, AbstractFrame> frames;
150
151    /** Frame for the currently processed AST. */
152    private AbstractFrame current;
153
154    /** Whether we should check fields usage. */
155    private boolean checkFields = true;
156    /** Whether we should check methods usage. */
157    private boolean checkMethods = true;
158    /** Whether we should check only overlapping by variables or arguments. */
159    private boolean validateOnlyOverlapping = true;
160
161    /**
162     * Setter for checkFields property.
163     * @param checkFields should we check fields usage or not.
164     */
165    public void setCheckFields(boolean checkFields) {
166        this.checkFields = checkFields;
167    }
168
169    /**
170     * Setter for checkMethods property.
171     * @param checkMethods should we check methods usage or not.
172     */
173    public void setCheckMethods(boolean checkMethods) {
174        this.checkMethods = checkMethods;
175    }
176
177    /**
178     * Setter for validateOnlyOverlapping property.
179     * @param validateOnlyOverlapping should we check only overlapping by variables or arguments.
180     */
181    public void setValidateOnlyOverlapping(boolean validateOnlyOverlapping) {
182        this.validateOnlyOverlapping = validateOnlyOverlapping;
183    }
184
185    @Override
186    public int[] getDefaultTokens() {
187        return getRequiredTokens();
188    }
189
190    @Override
191    public int[] getRequiredTokens() {
192        return new int[] {
193            TokenTypes.CLASS_DEF,
194            TokenTypes.INTERFACE_DEF,
195            TokenTypes.ENUM_DEF,
196            TokenTypes.CTOR_DEF,
197            TokenTypes.METHOD_DEF,
198            TokenTypes.SLIST,
199            TokenTypes.IDENT,
200        };
201    }
202
203    @Override
204    public int[] getAcceptableTokens() {
205        return getRequiredTokens();
206    }
207
208    @Override
209    public void beginTree(DetailAST rootAST) {
210        frames = new HashMap<>();
211        current = null;
212
213        final Deque<AbstractFrame> frameStack = new LinkedList<>();
214        DetailAST curNode = rootAST;
215        while (curNode != null) {
216            collectDeclarations(frameStack, curNode);
217            DetailAST toVisit = curNode.getFirstChild();
218            while (curNode != null && toVisit == null) {
219                endCollectingDeclarations(frameStack, curNode);
220                toVisit = curNode.getNextSibling();
221                if (toVisit == null) {
222                    curNode = curNode.getParent();
223                }
224            }
225            curNode = toVisit;
226        }
227    }
228
229    @Override
230    public void visitToken(DetailAST ast) {
231        switch (ast.getType()) {
232            case TokenTypes.IDENT :
233                processIdent(ast);
234                break;
235            case TokenTypes.CLASS_DEF :
236            case TokenTypes.INTERFACE_DEF :
237            case TokenTypes.ENUM_DEF :
238            case TokenTypes.ANNOTATION_DEF :
239            case TokenTypes.SLIST :
240            case TokenTypes.METHOD_DEF :
241            case TokenTypes.CTOR_DEF :
242                current = frames.get(ast);
243                break;
244            default :
245                // do nothing
246        }
247    }
248
249    /**
250     * Checks if a given IDENT is method call or field name which
251     * requires explicit {@code this} qualifier.
252     * @param ast IDENT to check.
253     */
254    private void processIdent(DetailAST ast) {
255        final int parentType = ast.getParent().getType();
256        switch (parentType) {
257            case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR:
258            case TokenTypes.ANNOTATION:
259            case TokenTypes.ANNOTATION_FIELD_DEF:
260                // no need to check annotations content
261                break;
262            case TokenTypes.METHOD_CALL:
263                if (checkMethods) {
264                    final AbstractFrame frame = getMethodWithoutThis(ast);
265                    if (frame != null) {
266                        logViolation(MSG_METHOD, ast, frame);
267                    }
268                }
269                break;
270            default:
271                if (checkFields) {
272                    final AbstractFrame frame = getFieldWithoutThis(ast, parentType);
273                    if (frame != null) {
274                        logViolation(MSG_VARIABLE, ast, frame);
275                    }
276                }
277                break;
278        }
279    }
280
281    /**
282     * Helper method to log a LocalizedMessage.
283     * @param ast a node to get line id column numbers associated with the message.
284     * @param msgKey key to locale message format.
285     * @param frame the class frame where the violation is found.
286     */
287    private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) {
288        if (frame.getFrameName().equals(getNearestClassFrameName())) {
289            log(ast, msgKey, ast.getText(), "");
290        }
291        else if (!(frame instanceof AnonymousClassFrame)) {
292            log(ast, msgKey, ast.getText(), frame.getFrameName() + '.');
293        }
294    }
295
296    /**
297     * Returns the frame where the field is declared, if the given field is used without
298     * 'this', and null otherwise.
299     * @param ast field definition ast token.
300     * @param parentType type of the parent.
301     * @return the frame where the field is declared, if the given field is used without
302     *         'this' and null otherwise.
303     */
304    private AbstractFrame getFieldWithoutThis(DetailAST ast, int parentType) {
305        final boolean importOrPackage = ScopeUtils.getSurroundingScope(ast) == null;
306        final boolean methodNameInMethodCall = parentType == TokenTypes.DOT
307                && ast.getPreviousSibling() != null;
308        final boolean typeName = parentType == TokenTypes.TYPE
309                || parentType == TokenTypes.LITERAL_NEW;
310        AbstractFrame frame = null;
311
312        if (!importOrPackage
313                && !methodNameInMethodCall
314                && !typeName
315                && !isDeclarationToken(parentType)
316                && !isLambdaParameter(ast)) {
317            final AbstractFrame fieldFrame = findClassFrame(ast, false);
318
319            if (fieldFrame != null && ((ClassFrame) fieldFrame).hasInstanceMember(ast)) {
320                frame = getClassFrameWhereViolationIsFound(ast);
321            }
322        }
323        return frame;
324    }
325
326    /**
327     * Parses the next AST for declarations.
328     * @param frameStack stack containing the FrameTree being built.
329     * @param ast AST to parse.
330     */
331    private static void collectDeclarations(Deque<AbstractFrame> frameStack, DetailAST ast) {
332        final AbstractFrame frame = frameStack.peek();
333        switch (ast.getType()) {
334            case TokenTypes.VARIABLE_DEF :
335                collectVariableDeclarations(ast, frame);
336                break;
337            case TokenTypes.PARAMETER_DEF :
338                if (!CheckUtils.isReceiverParameter(ast)
339                        && !isLambdaParameter(ast)) {
340                    final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT);
341                    frame.addIdent(parameterIdent);
342                }
343                break;
344            case TokenTypes.CLASS_DEF :
345            case TokenTypes.INTERFACE_DEF :
346            case TokenTypes.ENUM_DEF :
347            case TokenTypes.ANNOTATION_DEF :
348                final DetailAST classFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
349                frameStack.addFirst(new ClassFrame(frame, classFrameNameIdent));
350                break;
351            case TokenTypes.SLIST :
352                frameStack.addFirst(new BlockFrame(frame, ast));
353                break;
354            case TokenTypes.METHOD_DEF :
355                final DetailAST methodFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
356                final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
357                if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
358                    ((ClassFrame) frame).addInstanceMethod(methodFrameNameIdent);
359                }
360                else {
361                    ((ClassFrame) frame).addStaticMethod(methodFrameNameIdent);
362                }
363                frameStack.addFirst(new MethodFrame(frame, methodFrameNameIdent));
364                break;
365            case TokenTypes.CTOR_DEF :
366                final DetailAST ctorFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
367                frameStack.addFirst(new ConstructorFrame(frame, ctorFrameNameIdent));
368                break;
369            case TokenTypes.LITERAL_NEW:
370                if (isAnonymousClassDef(ast)) {
371                    frameStack.addFirst(new AnonymousClassFrame(frame,
372                            ast.getFirstChild().toString()));
373                }
374                break;
375            default:
376                // do nothing
377        }
378    }
379
380    /**
381     * Collects variable declarations.
382     * @param ast variable token.
383     * @param frame current frame.
384     */
385    private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) {
386        final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
387        if (frame.getType() == FrameType.CLASS_FRAME) {
388            final DetailAST mods =
389                    ast.findFirstToken(TokenTypes.MODIFIERS);
390            if (ScopeUtils.isInInterfaceBlock(ast)
391                    || mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
392                ((ClassFrame) frame).addStaticMember(ident);
393            }
394            else {
395                ((ClassFrame) frame).addInstanceMember(ident);
396            }
397        }
398        else {
399            frame.addIdent(ident);
400        }
401    }
402
403    /**
404     * Ends parsing of the AST for declarations.
405     * @param frameStack Stack containing the FrameTree being built.
406     * @param ast AST that was parsed.
407     */
408    private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, DetailAST ast) {
409        switch (ast.getType()) {
410            case TokenTypes.CLASS_DEF :
411            case TokenTypes.INTERFACE_DEF :
412            case TokenTypes.ENUM_DEF :
413            case TokenTypes.ANNOTATION_DEF :
414            case TokenTypes.SLIST :
415            case TokenTypes.METHOD_DEF :
416            case TokenTypes.CTOR_DEF :
417                frames.put(ast, frameStack.poll());
418                break;
419            case TokenTypes.LITERAL_NEW :
420                if (isAnonymousClassDef(ast)) {
421                    frames.put(ast, frameStack.poll());
422                }
423                break;
424            default :
425                // do nothing
426        }
427    }
428
429    /**
430     * Whether the AST is a definition of an anonymous class.
431     * @param ast the AST to process.
432     * @return true if the AST is a definition of an anonymous class.
433     */
434    private static boolean isAnonymousClassDef(DetailAST ast) {
435        final DetailAST lastChild = ast.getLastChild();
436        return lastChild != null
437            && lastChild.getType() == TokenTypes.OBJBLOCK;
438    }
439
440    /**
441     * Returns the class frame where violation is found (where the field is used without 'this')
442     * or null otherwise.
443     * @param ast IDENT ast to check.
444     * @return the class frame where violation is found or null otherwise.
445     * @noinspection IfStatementWithIdenticalBranches
446     */
447    // -@cs[CyclomaticComplexity] Method already invokes too many methods that fully explain
448    // a logic, additional abstraction will not make logic/algorithm more readable.
449    private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) {
450        AbstractFrame frameWhereViolationIsFound = null;
451        final AbstractFrame variableDeclarationFrame = findFrame(ast, false);
452        final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType();
453        final DetailAST prevSibling = ast.getPreviousSibling();
454        if (variableDeclarationFrameType == FrameType.CLASS_FRAME
455                && !validateOnlyOverlapping
456                && prevSibling == null
457                && canBeReferencedFromStaticContext(ast)) {
458            frameWhereViolationIsFound = variableDeclarationFrame;
459        }
460        else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) {
461            if (isOverlappingByArgument(ast)) {
462                if (!isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
463                        && !isReturnedVariable(variableDeclarationFrame, ast)
464                        && canBeReferencedFromStaticContext(ast)
465                        && canAssignValueToClassField(ast)) {
466                    frameWhereViolationIsFound = findFrame(ast, true);
467                }
468            }
469            else if (!validateOnlyOverlapping
470                     && prevSibling == null
471                     && isAssignToken(ast.getParent().getType())
472                     && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
473                     && canBeReferencedFromStaticContext(ast)
474                     && canAssignValueToClassField(ast)) {
475                frameWhereViolationIsFound = findFrame(ast, true);
476
477            }
478        }
479        else if (variableDeclarationFrameType == FrameType.CTOR_FRAME
480                 && isOverlappingByArgument(ast)
481                 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) {
482            frameWhereViolationIsFound = findFrame(ast, true);
483        }
484        else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME
485                    && isOverlappingByLocalVariable(ast)
486                    && canAssignValueToClassField(ast)
487                    && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
488                    && !isReturnedVariable(variableDeclarationFrame, ast)
489                    && canBeReferencedFromStaticContext(ast)) {
490            frameWhereViolationIsFound = findFrame(ast, true);
491        }
492        return frameWhereViolationIsFound;
493    }
494
495    /**
496     * Checks whether user arranges 'this' for variable in method, constructor, or block on his own.
497     * @param currentFrame current frame.
498     * @param ident ident token.
499     * @return true if user arranges 'this' for variable in method, constructor,
500     *         or block on his own.
501     */
502    private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame,
503                                                          DetailAST ident) {
504        final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
505        final DetailAST definitionToken = blockFrameNameIdent.getParent();
506        final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
507        final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
508
509        boolean userDefinedArrangementOfThis = false;
510
511        final Set<DetailAST> variableUsagesInsideBlock =
512            getAllTokensWhichAreEqualToCurrent(definitionToken, ident,
513                blockEndToken.getLineNo());
514
515        for (DetailAST variableUsage : variableUsagesInsideBlock) {
516            final DetailAST prevSibling = variableUsage.getPreviousSibling();
517            if (prevSibling != null
518                    && prevSibling.getType() == TokenTypes.LITERAL_THIS) {
519                userDefinedArrangementOfThis = true;
520                break;
521            }
522        }
523        return userDefinedArrangementOfThis;
524    }
525
526    /**
527     * Returns the token which ends the code block.
528     * @param blockNameIdent block name identifier.
529     * @param blockStartToken token which starts the block.
530     * @return the token which ends the code block.
531     */
532    private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) {
533        DetailAST blockEndToken = null;
534        final DetailAST blockNameIdentParent = blockNameIdent.getParent();
535        if (blockNameIdentParent.getType() == TokenTypes.CASE_GROUP) {
536            blockEndToken = blockNameIdentParent.getNextSibling();
537        }
538        else {
539            final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent,
540                    TokenTypes.RCURLY);
541            for (DetailAST currentRcurly : rcurlyTokens) {
542                final DetailAST parent = currentRcurly.getParent();
543                if (blockStartToken.getLineNo() == parent.getLineNo()) {
544                    blockEndToken = currentRcurly;
545                }
546            }
547        }
548        return blockEndToken;
549    }
550
551    /**
552     * Checks whether the current variable is returned from the method.
553     * @param currentFrame current frame.
554     * @param ident variable ident token.
555     * @return true if the current variable is returned from the method.
556     */
557    private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) {
558        final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
559        final DetailAST definitionToken = blockFrameNameIdent.getParent();
560        final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
561        final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
562
563        final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken,
564            TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo());
565
566        boolean returnedVariable = false;
567        for (DetailAST returnToken : returnsInsideBlock) {
568            returnedVariable = returnToken.findAll(ident).hasMoreNodes();
569            if (returnedVariable) {
570                break;
571            }
572        }
573        return returnedVariable;
574    }
575
576    /**
577     * Checks whether a field can be referenced from a static context.
578     * @param ident ident token.
579     * @return true if field can be referenced from a static context.
580     */
581    private boolean canBeReferencedFromStaticContext(DetailAST ident) {
582        AbstractFrame variableDeclarationFrame = findFrame(ident, false);
583        boolean staticInitializationBlock = false;
584        while (variableDeclarationFrame.getType() == FrameType.BLOCK_FRAME) {
585            final DetailAST blockFrameNameIdent = variableDeclarationFrame.getFrameNameIdent();
586            final DetailAST definitionToken = blockFrameNameIdent.getParent();
587            if (definitionToken.getType() == TokenTypes.STATIC_INIT) {
588                staticInitializationBlock = true;
589                break;
590            }
591            variableDeclarationFrame = variableDeclarationFrame.getParent();
592        }
593
594        boolean staticContext = false;
595        if (staticInitializationBlock) {
596            staticContext = true;
597        }
598        else {
599            if (variableDeclarationFrame.getType() == FrameType.CLASS_FRAME) {
600                final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident);
601                if (codeBlockDefinition != null) {
602                    final DetailAST modifiers = codeBlockDefinition.getFirstChild();
603                    staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT
604                        || modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
605                }
606            }
607            else {
608                final DetailAST frameNameIdent = variableDeclarationFrame.getFrameNameIdent();
609                final DetailAST definitionToken = frameNameIdent.getParent();
610                staticContext = definitionToken.findFirstToken(TokenTypes.MODIFIERS)
611                        .findFirstToken(TokenTypes.LITERAL_STATIC) != null;
612            }
613        }
614        return !staticContext;
615    }
616
617    /**
618     * Returns code block definition token for current identifier.
619     * @param ident ident token.
620     * @return code block definition token for current identifier or null if code block
621     *         definition was not found.
622     */
623    private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) {
624        DetailAST parent = ident.getParent();
625        while (parent != null
626               && parent.getType() != TokenTypes.METHOD_DEF
627               && parent.getType() != TokenTypes.CTOR_DEF
628               && parent.getType() != TokenTypes.STATIC_INIT) {
629            parent = parent.getParent();
630        }
631        return parent;
632    }
633
634    /**
635     * Checks whether a value can be assigned to a field.
636     * A value can be assigned to a final field only in constructor block. If there is a method
637     * block, value assignment can be performed only to non final field.
638     * @param ast an identifier token.
639     * @return true if a value can be assigned to a field.
640     */
641    private boolean canAssignValueToClassField(DetailAST ast) {
642        final AbstractFrame fieldUsageFrame = findFrame(ast, false);
643        final boolean fieldUsageInConstructor = isInsideConstructorFrame(fieldUsageFrame);
644
645        final AbstractFrame declarationFrame = findFrame(ast, true);
646        final boolean finalField = ((ClassFrame) declarationFrame).hasFinalField(ast);
647
648        return fieldUsageInConstructor || !finalField;
649    }
650
651    /**
652     * Checks whether a field usage frame is inside constructor frame.
653     * @param frame frame, where field is used.
654     * @return true if the field usage frame is inside constructor frame.
655     */
656    private static boolean isInsideConstructorFrame(AbstractFrame frame) {
657        boolean assignmentInConstructor = false;
658        AbstractFrame fieldUsageFrame = frame;
659        if (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
660            while (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
661                fieldUsageFrame = fieldUsageFrame.getParent();
662            }
663            if (fieldUsageFrame.getType() == FrameType.CTOR_FRAME) {
664                assignmentInConstructor = true;
665            }
666        }
667        return assignmentInConstructor;
668    }
669
670    /**
671     * Checks whether an overlapping by method or constructor argument takes place.
672     * @param ast an identifier.
673     * @return true if an overlapping by method or constructor argument takes place.
674     */
675    private boolean isOverlappingByArgument(DetailAST ast) {
676        boolean overlapping = false;
677        final DetailAST parent = ast.getParent();
678        final DetailAST sibling = ast.getNextSibling();
679        if (sibling != null && isAssignToken(parent.getType())) {
680            if (isCompoundAssignToken(parent.getType())) {
681                overlapping = true;
682            }
683            else {
684                final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
685                final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
686                overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
687            }
688        }
689        return overlapping;
690    }
691
692    /**
693     * Checks whether an overlapping by local variable takes place.
694     * @param ast an identifier.
695     * @return true if an overlapping by local variable takes place.
696     */
697    private boolean isOverlappingByLocalVariable(DetailAST ast) {
698        boolean overlapping = false;
699        final DetailAST parent = ast.getParent();
700        final DetailAST sibling = ast.getNextSibling();
701        if (sibling != null && isAssignToken(parent.getType())) {
702            final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
703            final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
704            overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
705        }
706        return overlapping;
707    }
708
709    /**
710     * Collects all tokens of specific type starting with the current ast node.
711     * @param ast ast node.
712     * @param tokenType token type.
713     * @return a set of all tokens of specific type starting with the current ast node.
714     */
715    private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
716        DetailAST vertex = ast;
717        final Set<DetailAST> result = new HashSet<>();
718        final Deque<DetailAST> stack = new ArrayDeque<>();
719        while (vertex != null || !stack.isEmpty()) {
720            if (!stack.isEmpty()) {
721                vertex = stack.pop();
722            }
723            while (vertex != null) {
724                if (vertex.getType() == tokenType) {
725                    result.add(vertex);
726                }
727                if (vertex.getNextSibling() != null) {
728                    stack.push(vertex.getNextSibling());
729                }
730                vertex = vertex.getFirstChild();
731            }
732        }
733        return result;
734    }
735
736    /**
737     * Collects all tokens of specific type starting with the current ast node and which line
738     * number is lower or equal to the end line number.
739     * @param ast ast node.
740     * @param tokenType token type.
741     * @param endLineNumber end line number.
742     * @return a set of all tokens of specific type starting with the current ast node and which
743     *         line number is lower or equal to the end line number.
744     */
745    private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType,
746                                                     int endLineNumber) {
747        DetailAST vertex = ast;
748        final Set<DetailAST> result = new HashSet<>();
749        final Deque<DetailAST> stack = new ArrayDeque<>();
750        while (vertex != null || !stack.isEmpty()) {
751            if (!stack.isEmpty()) {
752                vertex = stack.pop();
753            }
754            while (vertex != null) {
755                if (tokenType == vertex.getType()
756                    && vertex.getLineNo() <= endLineNumber) {
757                    result.add(vertex);
758                }
759                if (vertex.getNextSibling() != null) {
760                    stack.push(vertex.getNextSibling());
761                }
762                vertex = vertex.getFirstChild();
763            }
764        }
765        return result;
766    }
767
768    /**
769     * Collects all tokens which are equal to current token starting with the current ast node and
770     * which line number is lower or equal to the end line number.
771     * @param ast ast node.
772     * @param token token.
773     * @param endLineNumber end line number.
774     * @return a set of tokens which are equal to current token starting with the current ast node
775     *         and which line number is lower or equal to the end line number.
776     */
777    private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token,
778                                                                     int endLineNumber) {
779        DetailAST vertex = ast;
780        final Set<DetailAST> result = new HashSet<>();
781        final Deque<DetailAST> stack = new ArrayDeque<>();
782        while (vertex != null || !stack.isEmpty()) {
783            if (!stack.isEmpty()) {
784                vertex = stack.pop();
785            }
786            while (vertex != null) {
787                if (token.equals(vertex)
788                        && vertex.getLineNo() <= endLineNumber) {
789                    result.add(vertex);
790                }
791                if (vertex.getNextSibling() != null) {
792                    stack.push(vertex.getNextSibling());
793                }
794                vertex = vertex.getFirstChild();
795            }
796        }
797        return result;
798    }
799
800    /**
801     * Returns the frame where the method is declared, if the given method is used without
802     * 'this' and null otherwise.
803     * @param ast the IDENT ast of the name to check.
804     * @return the frame where the method is declared, if the given method is used without
805     *         'this' and null otherwise.
806     */
807    private AbstractFrame getMethodWithoutThis(DetailAST ast) {
808        AbstractFrame result = null;
809        if (!validateOnlyOverlapping) {
810            final AbstractFrame frame = findFrame(ast, true);
811            if (frame != null
812                    && ((ClassFrame) frame).hasInstanceMethod(ast)
813                    && !((ClassFrame) frame).hasStaticMethod(ast)) {
814                result = frame;
815            }
816        }
817        return result;
818    }
819
820    /**
821     * Find the class frame containing declaration.
822     * @param name IDENT ast of the declaration to find.
823     * @param lookForMethod whether we are looking for a method name.
824     * @return AbstractFrame containing declaration or null.
825     */
826    private AbstractFrame findClassFrame(DetailAST name, boolean lookForMethod) {
827        AbstractFrame frame = current;
828
829        while (true) {
830            frame = findFrame(frame, name, lookForMethod);
831
832            if (frame == null || frame instanceof ClassFrame) {
833                break;
834            }
835
836            frame = frame.getParent();
837        }
838
839        return frame;
840    }
841
842    /**
843     * Find frame containing declaration.
844     * @param name IDENT ast of the declaration to find.
845     * @param lookForMethod whether we are looking for a method name.
846     * @return AbstractFrame containing declaration or null.
847     */
848    private AbstractFrame findFrame(DetailAST name, boolean lookForMethod) {
849        return findFrame(current, name, lookForMethod);
850    }
851
852    /**
853     * Find frame containing declaration.
854     * @param frame The parent frame to searching in.
855     * @param name IDENT ast of the declaration to find.
856     * @param lookForMethod whether we are looking for a method name.
857     * @return AbstractFrame containing declaration or null.
858     */
859    private static AbstractFrame findFrame(AbstractFrame frame, DetailAST name,
860            boolean lookForMethod) {
861        final AbstractFrame result;
862        if (frame == null) {
863            result = null;
864        }
865        else {
866            result = frame.getIfContains(name, lookForMethod);
867        }
868        return result;
869    }
870
871    /**
872     * Check that token is related to Definition tokens.
873     * @param parentType token Type.
874     * @return true if token is related to Definition Tokens.
875     */
876    private static boolean isDeclarationToken(int parentType) {
877        return DECLARATION_TOKENS.contains(parentType);
878    }
879
880    /**
881     * Check that token is related to assign tokens.
882     * @param tokenType token type.
883     * @return true if token is related to assign tokens.
884     */
885    private static boolean isAssignToken(int tokenType) {
886        return ASSIGN_TOKENS.contains(tokenType);
887    }
888
889    /**
890     * Check that token is related to compound assign tokens.
891     * @param tokenType token type.
892     * @return true if token is related to compound assign tokens.
893     */
894    private static boolean isCompoundAssignToken(int tokenType) {
895        return COMPOUND_ASSIGN_TOKENS.contains(tokenType);
896    }
897
898    /**
899     * Gets the name of the nearest parent ClassFrame.
900     * @return the name of the nearest parent ClassFrame.
901     */
902    private String getNearestClassFrameName() {
903        AbstractFrame frame = current;
904        while (frame.getType() != FrameType.CLASS_FRAME) {
905            frame = frame.getParent();
906        }
907        return frame.getFrameName();
908    }
909
910    /**
911     * Checks if the token is a Lambda parameter.
912     * @param ast the {@code DetailAST} value of the token to be checked
913     * @return true if the token is a Lambda parameter
914     */
915    private static boolean isLambdaParameter(DetailAST ast) {
916        DetailAST parent;
917        for (parent = ast.getParent(); parent != null; parent = parent.getParent()) {
918            if (parent.getType() == TokenTypes.LAMBDA) {
919                break;
920            }
921        }
922        final boolean isLambdaParameter;
923        if (parent == null) {
924            isLambdaParameter = false;
925        }
926        else if (ast.getType() == TokenTypes.PARAMETER_DEF) {
927            isLambdaParameter = true;
928        }
929        else {
930            final DetailAST lambdaParameters = parent.findFirstToken(TokenTypes.PARAMETERS);
931            if (lambdaParameters == null) {
932                isLambdaParameter = parent.getFirstChild().getText().equals(ast.getText());
933            }
934            else {
935                isLambdaParameter = TokenUtils.findFirstTokenByPredicate(lambdaParameters,
936                    paramDef -> {
937                        final DetailAST param = paramDef.findFirstToken(TokenTypes.IDENT);
938                        return param != null && param.getText().equals(ast.getText());
939                    }).isPresent();
940            }
941        }
942        return isLambdaParameter;
943    }
944
945    /** An AbstractFrame type. */
946    private enum FrameType {
947        /** Class frame type. */
948        CLASS_FRAME,
949        /** Constructor frame type. */
950        CTOR_FRAME,
951        /** Method frame type. */
952        METHOD_FRAME,
953        /** Block frame type. */
954        BLOCK_FRAME,
955    }
956
957    /**
958     * A declaration frame.
959     * @author Stephen Bloch
960     * @author Andrei Selkin
961     */
962    private abstract static class AbstractFrame {
963        /** Set of name of variables declared in this frame. */
964        private final Set<DetailAST> varIdents;
965
966        /** Parent frame. */
967        private final AbstractFrame parent;
968
969        /** Name identifier token. */
970        private final DetailAST frameNameIdent;
971
972        /**
973         * Constructor -- invokable only via super() from subclasses.
974         * @param parent parent frame.
975         * @param ident frame name ident.
976         */
977        protected AbstractFrame(AbstractFrame parent, DetailAST ident) {
978            this.parent = parent;
979            frameNameIdent = ident;
980            varIdents = new HashSet<>();
981        }
982
983        /**
984         * Get the type of the frame.
985         * @return a FrameType.
986         */
987        protected abstract FrameType getType();
988
989        /**
990         * Add a name to the frame.
991         * @param identToAdd the name we're adding.
992         */
993        private void addIdent(DetailAST identToAdd) {
994            varIdents.add(identToAdd);
995        }
996
997        protected AbstractFrame getParent() {
998            return parent;
999        }
1000
1001        protected String getFrameName() {
1002            return frameNameIdent.getText();
1003        }
1004
1005        public DetailAST getFrameNameIdent() {
1006            return frameNameIdent;
1007        }
1008
1009        /**
1010         * Check whether the frame contains a field or a variable with the given name.
1011         * @param nameToFind the IDENT ast of the name we're looking for.
1012         * @return whether it was found.
1013         */
1014        protected boolean containsFieldOrVariable(DetailAST nameToFind) {
1015            return containsFieldOrVariableDef(varIdents, nameToFind);
1016        }
1017
1018        /**
1019         * Check whether the frame contains a given name.
1020         * @param nameToFind IDENT ast of the name we're looking for.
1021         * @param lookForMethod whether we are looking for a method name.
1022         * @return whether it was found.
1023         */
1024        protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
1025            final AbstractFrame frame;
1026
1027            if (!lookForMethod
1028                && containsFieldOrVariable(nameToFind)) {
1029                frame = this;
1030            }
1031            else {
1032                frame = parent.getIfContains(nameToFind, lookForMethod);
1033            }
1034            return frame;
1035        }
1036
1037        /**
1038         * Whether the set contains a declaration with the text of the specified
1039         * IDENT ast and it is declared in a proper position.
1040         * @param set the set of declarations.
1041         * @param ident the specified IDENT ast.
1042         * @return true if the set contains a declaration with the text of the specified
1043         *         IDENT ast and it is declared in a proper position.
1044         */
1045        protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) {
1046            boolean result = false;
1047            for (DetailAST ast: set) {
1048                if (isProperDefinition(ident, ast)) {
1049                    result = true;
1050                    break;
1051                }
1052            }
1053            return result;
1054        }
1055
1056        /**
1057         * Whether the definition is correspondent to the IDENT.
1058         * @param ident the IDENT ast to check.
1059         * @param ast the IDENT ast of the definition to check.
1060         * @return true if ast is correspondent to ident.
1061         */
1062        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1063            final String nameToFind = ident.getText();
1064            return nameToFind.equals(ast.getText())
1065                && checkPosition(ast, ident);
1066        }
1067
1068        /**
1069         * Whether the declaration is located before the checked ast.
1070         * @param ast1 the IDENT ast of the declaration.
1071         * @param ast2 the IDENT ast to check.
1072         * @return true, if the declaration is located before the checked ast.
1073         */
1074        private static boolean checkPosition(DetailAST ast1, DetailAST ast2) {
1075            boolean result = false;
1076            if (ast1.getLineNo() < ast2.getLineNo()
1077                    || ast1.getLineNo() == ast2.getLineNo()
1078                    && ast1.getColumnNo() < ast2.getColumnNo()) {
1079                result = true;
1080            }
1081            return result;
1082        }
1083    }
1084
1085    /**
1086     * A frame initiated at method definition; holds a method definition token.
1087     * @author Stephen Bloch
1088     * @author Andrei Selkin
1089     */
1090    private static class MethodFrame extends AbstractFrame {
1091
1092        /**
1093         * Creates method frame.
1094         * @param parent parent frame.
1095         * @param ident method name identifier token.
1096         */
1097        protected MethodFrame(AbstractFrame parent, DetailAST ident) {
1098            super(parent, ident);
1099        }
1100
1101        @Override
1102        protected FrameType getType() {
1103            return FrameType.METHOD_FRAME;
1104        }
1105    }
1106
1107    /**
1108     * A frame initiated at constructor definition.
1109     * @author Andrei Selkin
1110     */
1111    private static class ConstructorFrame extends AbstractFrame {
1112
1113        /**
1114         * Creates a constructor frame.
1115         * @param parent parent frame.
1116         * @param ident frame name ident.
1117         */
1118        protected ConstructorFrame(AbstractFrame parent, DetailAST ident) {
1119            super(parent, ident);
1120        }
1121
1122        @Override
1123        protected FrameType getType() {
1124            return FrameType.CTOR_FRAME;
1125        }
1126    }
1127
1128    /**
1129     * A frame initiated at class, enum or interface definition; holds instance variable names.
1130     * @author Stephen Bloch
1131     * @author Andrei Selkin
1132     */
1133    private static class ClassFrame extends AbstractFrame {
1134        /** Set of idents of instance members declared in this frame. */
1135        private final Set<DetailAST> instanceMembers;
1136        /** Set of idents of instance methods declared in this frame. */
1137        private final Set<DetailAST> instanceMethods;
1138        /** Set of idents of variables declared in this frame. */
1139        private final Set<DetailAST> staticMembers;
1140        /** Set of idents of static methods declared in this frame. */
1141        private final Set<DetailAST> staticMethods;
1142
1143        /**
1144         * Creates new instance of ClassFrame.
1145         * @param parent parent frame.
1146         * @param ident frame name ident.
1147         */
1148        ClassFrame(AbstractFrame parent, DetailAST ident) {
1149            super(parent, ident);
1150            instanceMembers = new HashSet<>();
1151            instanceMethods = new HashSet<>();
1152            staticMembers = new HashSet<>();
1153            staticMethods = new HashSet<>();
1154        }
1155
1156        @Override
1157        protected FrameType getType() {
1158            return FrameType.CLASS_FRAME;
1159        }
1160
1161        /**
1162         * Adds static member's ident.
1163         * @param ident an ident of static member of the class.
1164         */
1165        public void addStaticMember(final DetailAST ident) {
1166            staticMembers.add(ident);
1167        }
1168
1169        /**
1170         * Adds static method's name.
1171         * @param ident an ident of static method of the class.
1172         */
1173        public void addStaticMethod(final DetailAST ident) {
1174            staticMethods.add(ident);
1175        }
1176
1177        /**
1178         * Adds instance member's ident.
1179         * @param ident an ident of instance member of the class.
1180         */
1181        public void addInstanceMember(final DetailAST ident) {
1182            instanceMembers.add(ident);
1183        }
1184
1185        /**
1186         * Adds instance method's name.
1187         * @param ident an ident of instance method of the class.
1188         */
1189        public void addInstanceMethod(final DetailAST ident) {
1190            instanceMethods.add(ident);
1191        }
1192
1193        /**
1194         * Checks if a given name is a known instance member of the class.
1195         * @param ident the IDENT ast of the name to check.
1196         * @return true is the given name is a name of a known
1197         *         instance member of the class.
1198         */
1199        public boolean hasInstanceMember(final DetailAST ident) {
1200            return containsFieldOrVariableDef(instanceMembers, ident);
1201        }
1202
1203        /**
1204         * Checks if a given name is a known instance method of the class.
1205         * @param ident the IDENT ast of the method call to check.
1206         * @return true if the given ast is correspondent to a known
1207         *         instance method of the class.
1208         */
1209        public boolean hasInstanceMethod(final DetailAST ident) {
1210            return containsMethodDef(instanceMethods, ident);
1211        }
1212
1213        /**
1214         * Checks if a given name is a known static method of the class.
1215         * @param ident the IDENT ast of the method call to check.
1216         * @return true is the given ast is correspondent to a known
1217         *         instance method of the class.
1218         */
1219        public boolean hasStaticMethod(final DetailAST ident) {
1220            return containsMethodDef(staticMethods, ident);
1221        }
1222
1223        /**
1224         * Checks whether given instance member has final modifier.
1225         * @param instanceMember an instance member of a class.
1226         * @return true if given instance member has final modifier.
1227         */
1228        public boolean hasFinalField(final DetailAST instanceMember) {
1229            boolean result = false;
1230            for (DetailAST member : instanceMembers) {
1231                final DetailAST mods = member.getParent().findFirstToken(TokenTypes.MODIFIERS);
1232                final boolean finalMod = mods.findFirstToken(TokenTypes.FINAL) != null;
1233                if (finalMod && member.equals(instanceMember)) {
1234                    result = true;
1235                    break;
1236                }
1237            }
1238            return result;
1239        }
1240
1241        @Override
1242        protected boolean containsFieldOrVariable(DetailAST nameToFind) {
1243            return containsFieldOrVariableDef(instanceMembers, nameToFind)
1244                    || containsFieldOrVariableDef(staticMembers, nameToFind);
1245        }
1246
1247        @Override
1248        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1249            final String nameToFind = ident.getText();
1250            return nameToFind.equals(ast.getText());
1251        }
1252
1253        @Override
1254        protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
1255            AbstractFrame frame = null;
1256
1257            if (lookForMethod && containsMethod(nameToFind)
1258                || containsFieldOrVariable(nameToFind)) {
1259                frame = this;
1260            }
1261            else if (getParent() != null) {
1262                frame = getParent().getIfContains(nameToFind, lookForMethod);
1263            }
1264            return frame;
1265        }
1266
1267        /**
1268         * Check whether the frame contains a given method.
1269         * @param methodToFind the AST of the method to find.
1270         * @return true, if a method with the same name and number of parameters is found.
1271         */
1272        private boolean containsMethod(DetailAST methodToFind) {
1273            return containsMethodDef(instanceMethods, methodToFind)
1274                || containsMethodDef(staticMethods, methodToFind);
1275        }
1276
1277        /**
1278         * Whether the set contains a method definition with the
1279         *     same name and number of parameters.
1280         * @param set the set of definitions.
1281         * @param ident the specified method call IDENT ast.
1282         * @return true if the set contains a definition with the
1283         *     same name and number of parameters.
1284         */
1285        private static boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) {
1286            boolean result = false;
1287            for (DetailAST ast: set) {
1288                if (isSimilarSignature(ident, ast)) {
1289                    result = true;
1290                    break;
1291                }
1292            }
1293            return result;
1294        }
1295
1296        /**
1297         * Whether the method definition has the same name and number of parameters.
1298         * @param ident the specified method call IDENT ast.
1299         * @param ast the ast of a method definition to compare with.
1300         * @return true if a method definition has the same name and number of parameters
1301         *     as the method call.
1302         */
1303        private static boolean isSimilarSignature(DetailAST ident, DetailAST ast) {
1304            boolean result = false;
1305            final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST);
1306            if (elistToken != null && ident.getText().equals(ast.getText())) {
1307                final int paramsNumber =
1308                    ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount();
1309                final int argsNumber = elistToken.getChildCount();
1310                result = paramsNumber == argsNumber;
1311            }
1312            return result;
1313        }
1314    }
1315
1316    /**
1317     * An anonymous class frame; holds instance variable names.
1318     */
1319    private static class AnonymousClassFrame extends ClassFrame {
1320
1321        /** The name of the frame. */
1322        private final String frameName;
1323
1324        /**
1325         * Creates anonymous class frame.
1326         * @param parent parent frame.
1327         * @param frameName name of the frame.
1328         */
1329        protected AnonymousClassFrame(AbstractFrame parent, String frameName) {
1330            super(parent, null);
1331            this.frameName = frameName;
1332        }
1333
1334        @Override
1335        protected String getFrameName() {
1336            return frameName;
1337        }
1338    }
1339
1340    /**
1341     * A frame initiated on entering a statement list; holds local variable names.
1342     * @author Stephen Bloch
1343     */
1344    private static class BlockFrame extends AbstractFrame {
1345
1346        /**
1347         * Creates block frame.
1348         * @param parent parent frame.
1349         * @param ident ident frame name ident.
1350         */
1351        protected BlockFrame(AbstractFrame parent, DetailAST ident) {
1352            super(parent, ident);
1353        }
1354
1355        @Override
1356        protected FrameType getType() {
1357            return FrameType.BLOCK_FRAME;
1358        }
1359    }
1360}