001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2015 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.Sets;
031import com.puppycrawl.tools.checkstyle.api.Check;
032import com.puppycrawl.tools.checkstyle.api.DetailAST;
033import com.puppycrawl.tools.checkstyle.api.TokenTypes;
034import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
035
036/**
037 * <p>Checks that code doesn't rely on the &quot;this&quot; default.
038 * That is references to instance variables and methods of the present
039 * object are explicitly of the form &quot;this.varName&quot; or
040 * &quot;this.methodName(args)&quot;.
041 *</p>
042 *
043 * <p>Examples of use:
044 * <pre>
045 * &lt;module name=&quot;RequireThis&quot;/&gt;
046 * </pre>
047 * An example of how to configure to check {@code this} qualifier for
048 * methods only:
049 * <pre>
050 * &lt;module name=&quot;RequireThis&quot;&gt;
051 *   &lt;property name=&quot;checkFields&quot; value=&quot;false&quot;/&gt;
052 *   &lt;property name=&quot;checkMethods&quot; value=&quot;true&quot;/&gt;
053 * &lt;/module&gt;
054 * </pre>
055 *
056 * <p>Limitations: I'm not currently doing anything about static variables
057 * or catch-blocks.  Static methods invoked on a class name seem to be OK;
058 * both the class name and the method name have a DOT parent.
059 * Non-static methods invoked on either this or a variable name seem to be
060 * OK, likewise.</p>
061 * <p>Much of the code for this check was cribbed from Rick Giles's
062 * {@code HiddenFieldCheck}.</p>
063 *
064 * @author Stephen Bloch
065 * @author o_sukhodolsky
066 */
067public class RequireThisCheck extends Check {
068
069    /**
070     * A key is pointing to the warning message text in "messages.properties"
071     * file.
072     */
073    public static final String MSG_METHOD = "require.this.method";
074
075    /**
076     * A key is pointing to the warning message text in "messages.properties"
077     * file.
078     */
079    public static final String MSG_VARIABLE = "require.this.variable";
080
081    /**
082     * Set of all declaration tokens.
083     */
084    private static final ImmutableSet<Integer> DECLARATION_TOKENS = ImmutableSet.of(
085            TokenTypes.VARIABLE_DEF,
086            TokenTypes.CTOR_DEF,
087            TokenTypes.METHOD_DEF,
088            TokenTypes.CLASS_DEF,
089            TokenTypes.ENUM_DEF,
090            TokenTypes.INTERFACE_DEF,
091            TokenTypes.PARAMETER_DEF,
092            TokenTypes.TYPE_ARGUMENT
093    );
094
095    /**
096     * Tree of all the parsed frames.
097     */
098    private Map<DetailAST, LexicalFrame> frames;
099
100    /**
101     * Frame for the currently processed AST.
102     */
103    private LexicalFrame current;
104
105    /** Whether we should check fields usage. */
106    private boolean checkFields = true;
107    /** Whether we should check methods usage. */
108    private boolean checkMethods = true;
109
110    /**
111     * Setter for checkFields property.
112     * @param checkFields should we check fields usage or not.
113     */
114    public void setCheckFields(boolean checkFields) {
115        this.checkFields = checkFields;
116    }
117
118    /**
119     * Setter for checkMethods property.
120     * @param checkMethods should we check methods usage or not.
121     */
122    public void setCheckMethods(boolean checkMethods) {
123        this.checkMethods = checkMethods;
124    }
125
126    @Override
127    public int[] getDefaultTokens() {
128        return getAcceptableTokens();
129    }
130
131    @Override
132    public int[] getRequiredTokens() {
133        return getAcceptableTokens();
134    }
135
136    @Override
137    public int[] getAcceptableTokens() {
138        return new int[] {
139            TokenTypes.CLASS_DEF,
140            TokenTypes.INTERFACE_DEF,
141            TokenTypes.ENUM_DEF,
142            TokenTypes.CTOR_DEF,
143            TokenTypes.METHOD_DEF,
144            TokenTypes.SLIST,
145            TokenTypes.IDENT,
146        };
147    }
148
149    @Override
150    public void beginTree(DetailAST rootAST) {
151        final Deque<LexicalFrame> frameStack = Lists.newLinkedList();
152        frameStack.add(new GlobalFrame());
153
154        frames = Maps.newHashMap();
155
156        DetailAST curNode = rootAST;
157        while (curNode != null) {
158            collectDeclarations(frameStack, curNode);
159            DetailAST toVisit = curNode.getFirstChild();
160            while (curNode != null && toVisit == null) {
161                endCollectingDeclarations(frameStack, curNode);
162                toVisit = curNode.getNextSibling();
163                if (toVisit == null) {
164                    curNode = curNode.getParent();
165                }
166            }
167            curNode = toVisit;
168        }
169    }
170
171    @Override
172    public void visitToken(DetailAST ast) {
173        switch (ast.getType()) {
174            case TokenTypes.IDENT :
175                processIdent(ast);
176                break;
177            case TokenTypes.CLASS_DEF :
178            case TokenTypes.INTERFACE_DEF :
179            case TokenTypes.ENUM_DEF :
180            case TokenTypes.ANNOTATION_DEF :
181            case TokenTypes.SLIST :
182            case TokenTypes.METHOD_DEF :
183            case TokenTypes.CTOR_DEF :
184                current = frames.get(ast);
185                break;
186            default :
187                // do nothing
188        }
189    }
190
191    /**
192     * Checks if a given IDENT is method call or field name which
193     * require explicit {@code this} qualifier.
194     *
195     * @param ast IDENT to check.
196     */
197    private void processIdent(DetailAST ast) {
198        final int parentType = ast.getParent().getType();
199        switch (parentType) {
200            case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR:
201            case TokenTypes.ANNOTATION:
202            case TokenTypes.ANNOTATION_FIELD_DEF:
203                // no need to check annotations content
204                break;
205            case TokenTypes.METHOD_CALL:
206                // let's check method calls
207                if (checkMethods && isClassMethod(ast)) {
208                    log(ast, MSG_METHOD, ast.getText());
209                }
210                break;
211            default:
212                if (checkFields) {
213                    processField(ast, parentType);
214                }
215                break;
216        }
217    }
218
219    /**
220     * Process validation of Field.
221     * @param ast field definition ast token
222     * @param parentType type of the parent
223     */
224    private void processField(DetailAST ast, int parentType) {
225        final boolean importOrPackage = ScopeUtils.getSurroundingScope(ast) == null;
226        final boolean methodNameInMethodCall = parentType == TokenTypes.DOT
227                && ast.getPreviousSibling() != null;
228        final boolean typeName = parentType == TokenTypes.TYPE
229                || parentType == TokenTypes.LITERAL_NEW;
230
231        if (!importOrPackage
232                && !methodNameInMethodCall
233                && !typeName
234                && !isDeclarationToken(parentType)
235                && isClassField(ast)) {
236            log(ast, MSG_VARIABLE, ast.getText());
237        }
238    }
239
240    /**
241     * Parse the next AST for declarations.
242     *
243     * @param frameStack Stack containing the FrameTree being built
244     * @param ast AST to parse
245     */
246    private static void collectDeclarations(Deque<LexicalFrame> frameStack,
247        DetailAST ast) {
248        final LexicalFrame frame = frameStack.peek();
249        switch (ast.getType()) {
250            case TokenTypes.VARIABLE_DEF :
251                collectVariableDeclarations(ast, frame);
252                break;
253            case TokenTypes.PARAMETER_DEF :
254                final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT);
255                frame.addIdent(parameterIdent);
256                break;
257            case TokenTypes.CLASS_DEF :
258            case TokenTypes.INTERFACE_DEF :
259            case TokenTypes.ENUM_DEF :
260            case TokenTypes.ANNOTATION_DEF :
261                final DetailAST classIdent = ast.findFirstToken(TokenTypes.IDENT);
262                frame.addIdent(classIdent);
263                frameStack.addFirst(new ClassFrame(frame));
264                break;
265            case TokenTypes.SLIST :
266                frameStack.addFirst(new BlockFrame(frame));
267                break;
268            case TokenTypes.METHOD_DEF :
269                final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
270                if (frame instanceof ClassFrame) {
271                    final DetailAST mods =
272                            ast.findFirstToken(TokenTypes.MODIFIERS);
273                    if (mods.branchContains(TokenTypes.LITERAL_STATIC)) {
274                        ((ClassFrame) frame).addStaticMethod(ident);
275                    }
276                    else {
277                        ((ClassFrame) frame).addInstanceMethod(ident);
278                    }
279                }
280                frameStack.addFirst(new MethodFrame(frame));
281                break;
282            case TokenTypes.CTOR_DEF :
283                frameStack.addFirst(new MethodFrame(frame));
284                break;
285            default:
286                // do nothing
287        }
288    }
289
290    /**
291     * Collect Variable Declarations.
292     * @param ast variable token
293     * @param frame current frame
294     */
295    private static void collectVariableDeclarations(DetailAST ast, LexicalFrame frame) {
296        final DetailAST ident =
297                ast.findFirstToken(TokenTypes.IDENT);
298        if (frame instanceof ClassFrame) {
299            final DetailAST mods =
300                    ast.findFirstToken(TokenTypes.MODIFIERS);
301            if (ScopeUtils.isInInterfaceBlock(ast)
302                    || mods.branchContains(TokenTypes.LITERAL_STATIC)) {
303                ((ClassFrame) frame).addStaticMember(ident);
304            }
305            else {
306                ((ClassFrame) frame).addInstanceMember(ident);
307            }
308        }
309        else {
310            frame.addIdent(ident);
311        }
312    }
313
314    /**
315     * End parsing of the AST for declarations.
316     *
317     * @param frameStack Stack containing the FrameTree being built
318     * @param ast AST that was parsed
319     */
320    private void endCollectingDeclarations(Queue<LexicalFrame> frameStack,
321        DetailAST ast) {
322        switch (ast.getType()) {
323            case TokenTypes.CLASS_DEF :
324            case TokenTypes.INTERFACE_DEF :
325            case TokenTypes.ENUM_DEF :
326            case TokenTypes.ANNOTATION_DEF :
327            case TokenTypes.SLIST :
328            case TokenTypes.METHOD_DEF :
329            case TokenTypes.CTOR_DEF :
330                frames.put(ast, frameStack.poll());
331                break;
332            default :
333                // do nothing
334        }
335    }
336
337    /**
338     * Check if given name is a name for class field in current environment.
339     * @param ident an IDENT ast to check
340     * @return true is the given name is name of member.
341     */
342    private boolean isClassField(DetailAST ident) {
343        final LexicalFrame frame = findFrame(ident, false);
344        return frame instanceof ClassFrame
345                && ((ClassFrame) frame).hasInstanceMember(ident);
346    }
347
348    /**
349     * Check if given name is a name for class method in current environment.
350     * @param ident the IDENT ast of the name to check
351     * @return true is the given name is name of method.
352     */
353    private boolean isClassMethod(DetailAST ident) {
354        final LexicalFrame frame = findFrame(ident, true);
355        return frame instanceof ClassFrame
356                && ((ClassFrame) frame).hasInstanceMethod(ident);
357    }
358
359    /**
360     * Find frame containing declaration.
361     * @param name IDENT ast of the declaration to find.
362     * @param lookForMethod whether we are looking for a method name.
363     * @return LexicalFrame containing declaration or null.
364     */
365    private LexicalFrame findFrame(DetailAST name, boolean lookForMethod) {
366        if (current == null) {
367            return null;
368        }
369        else {
370            return current.getIfContains(name, lookForMethod);
371        }
372    }
373
374    /**
375     * Check that token is related to Definition tokens.
376     * @param parentType token Type
377     * @return true if token is related to Definition Tokens
378     */
379    private static boolean isDeclarationToken(int parentType) {
380        return DECLARATION_TOKENS.contains(parentType);
381    }
382
383    /**
384     * A declaration frame.
385     * @author Stephen Bloch
386     */
387    private static class LexicalFrame {
388        /** Set of name of variables declared in this frame. */
389        private final Set<DetailAST> varIdents;
390
391        /**
392         * Parent frame.
393         */
394        private final LexicalFrame parent;
395
396        /**
397         * Constructor -- invokable only via super() from subclasses.
398         *
399         * @param parent parent frame
400         */
401        protected LexicalFrame(LexicalFrame parent) {
402            this.parent = parent;
403            varIdents = Sets.newHashSet();
404        }
405
406        /**
407         * Add a name to the frame.
408         * @param identToAdd the name we're adding
409         */
410        private void addIdent(DetailAST identToAdd) {
411            varIdents.add(identToAdd);
412        }
413
414        protected LexicalFrame getParent() {
415            return parent;
416        }
417
418        /** Check whether the frame contains a given name.
419         * @param nameToFind the IDENT ast of the name we're looking for
420         * @return whether it was found
421         */
422        boolean contains(DetailAST nameToFind) {
423            return containsName(varIdents, nameToFind);
424        }
425
426        /** Check whether the frame contains a given name.
427         * @param nameToFind IDENT ast of the name we're looking for.
428         * @param lookForMethod whether we are looking for a method name.
429         * @return whether it was found.
430         */
431        protected LexicalFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
432            LexicalFrame frame = null;
433
434            if (!lookForMethod
435                && contains(nameToFind)) {
436                frame = this;
437            }
438            else if (parent != null) {
439                frame = parent.getIfContains(nameToFind, lookForMethod);
440            }
441            return frame;
442        }
443
444        /**
445         * Whether the set contains a declaration with the text of the specified
446         * IDENT ast and it is declared in a proper position.
447         * @param set the set of declarations.
448         * @param ident the specified IDENT ast
449         * @return true if the set contains a declaration with the text of the specified
450         *         IDENT ast and it is declared in a proper position.
451         */
452        protected boolean containsName(Set<DetailAST> set, DetailAST ident) {
453            boolean result = false;
454            for (DetailAST ast: set) {
455                if (isProperDefinition(ident, ast)) {
456                    result = true;
457                    break;
458                }
459            }
460            return result;
461        }
462
463        /**
464         * Whether the definition is correspondent to the IDENT.
465         * @param ident the IDENT ast to check.
466         * @param ast the IDENT ast of the definition to check.
467         * @return true if ast is correspondent to ident.
468         */
469        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
470            final String nameToFind = ident.getText();
471            return nameToFind.equals(ast.getText())
472                && checkPosition(ast, ident);
473        }
474
475        /**
476         * Whether the declaration is located before the checked ast.
477         * @param ast1 the IDENT ast of the declaration.
478         * @param ast2 the IDENT ast to check.
479         * @return true, if the declaration is located before the checked ast.
480         */
481        private static boolean checkPosition(DetailAST ast1, DetailAST ast2) {
482            boolean result = false;
483            if (ast1.getLineNo() < ast2.getLineNo()
484                || ast1.getLineNo() == ast2.getLineNo()
485                && ast1.getColumnNo() < ast2.getColumnNo()) {
486                result = true;
487            }
488            return result;
489        }
490    }
491
492    /**
493     * The global frame; should hold only class names.
494     * @author Stephen Bloch
495     */
496    private static class GlobalFrame extends LexicalFrame {
497
498        /**
499         * Constructor for the root of the FrameTree.
500         */
501        protected GlobalFrame() {
502            super(null);
503        }
504    }
505
506    /**
507     * A frame initiated at method definition; holds parameter names.
508     * @author Stephen Bloch
509     */
510    private static class MethodFrame extends LexicalFrame {
511        /**
512         * Creates method frame.
513         * @param parent parent frame
514         */
515        protected MethodFrame(LexicalFrame parent) {
516            super(parent);
517        }
518    }
519
520    /**
521     * A frame initiated at class definition; holds instance variable
522     * names.  For the present, I'm not worried about other class names,
523     * method names, etc.
524     * @author Stephen Bloch
525     */
526    private static class ClassFrame extends LexicalFrame {
527        /** Set of idents of instance members declared in this frame. */
528        private final Set<DetailAST> instanceMembers;
529        /** Set of idents of instance methods declared in this frame. */
530        private final Set<DetailAST> instanceMethods;
531        /** Set of idents of variables declared in this frame. */
532        private final Set<DetailAST> staticMembers;
533        /** Set of idents of static methods declared in this frame. */
534        private final Set<DetailAST> staticMethods;
535
536        /**
537         * Creates new instance of ClassFrame.
538         * @param parent parent frame
539         */
540        ClassFrame(LexicalFrame parent) {
541            super(parent);
542            instanceMembers = Sets.newHashSet();
543            instanceMethods = Sets.newHashSet();
544            staticMembers = Sets.newHashSet();
545            staticMethods = Sets.newHashSet();
546        }
547
548        /**
549         * Adds static member's ident.
550         * @param ident an ident of static member of the class
551         */
552        public void addStaticMember(final DetailAST ident) {
553            staticMembers.add(ident);
554        }
555
556        /**
557         * Adds static method's name.
558         * @param ident an ident of static method of the class
559         */
560        public void addStaticMethod(final DetailAST ident) {
561            staticMethods.add(ident);
562        }
563
564        /**
565         * Adds instance member's ident.
566         * @param ident an ident of instance member of the class
567         */
568        public void addInstanceMember(final DetailAST ident) {
569            instanceMembers.add(ident);
570        }
571
572        /**
573         * Adds instance method's name.
574         * @param ident an ident of instance method of the class
575         */
576        public void addInstanceMethod(final DetailAST ident) {
577            instanceMethods.add(ident);
578        }
579
580        /**
581         * Checks if a given name is a known instance member of the class.
582         * @param ident the IDENT ast of the name to check
583         * @return true is the given name is a name of a known
584         *         instance member of the class
585         */
586        public boolean hasInstanceMember(final DetailAST ident) {
587            return containsName(instanceMembers, ident);
588        }
589
590        /**
591         * Checks if a given name is a known instance method of the class.
592         * @param ident the IDENT ast of the name to check
593         * @return true is the given name is a name of a known
594         *         instance method of the class
595         */
596        public boolean hasInstanceMethod(final DetailAST ident) {
597            return containsName(instanceMethods, ident);
598        }
599
600        @Override
601        boolean contains(DetailAST nameToFind) {
602            return super.contains(nameToFind)
603                    || containsName(instanceMembers, nameToFind)
604                    || containsName(instanceMethods, nameToFind)
605                    || containsName(staticMembers, nameToFind)
606                    || containsName(staticMethods, nameToFind);
607        }
608
609        @Override
610        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
611            final String nameToFind = ident.getText();
612            return nameToFind.equals(ast.getText());
613        }
614
615        @Override
616        protected LexicalFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
617            LexicalFrame frame;
618
619            if (contains(nameToFind)) {
620                frame = this;
621            }
622            else {
623                frame = getParent().getIfContains(nameToFind, lookForMethod);
624            }
625            return frame;
626        }
627    }
628
629    /**
630     * A frame initiated on entering a statement list; holds local variable
631     * names.  For the present, I'm not worried about other class names,
632     * method names, etc.
633     * @author Stephen Bloch
634     */
635    private static class BlockFrame extends LexicalFrame {
636
637        /**
638         * Creates block frame.
639         * @param parent parent frame
640         */
641        protected BlockFrame(LexicalFrame parent) {
642            super(parent);
643        }
644    }
645}