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