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