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