001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2018 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.LinkedList;
024
025import antlr.collections.AST;
026import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
027import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
028import com.puppycrawl.tools.checkstyle.api.DetailAST;
029import com.puppycrawl.tools.checkstyle.api.TokenTypes;
030import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
031
032/**
033 * <p>
034 * Abstract class for checking that an overriding method with no parameters
035 * invokes the super method.
036 * </p>
037 */
038@FileStatefulCheck
039public abstract class AbstractSuperCheck
040        extends AbstractCheck {
041
042    /**
043     * A key is pointing to the warning message text in "messages.properties"
044     * file.
045     */
046    public static final String MSG_KEY = "missing.super.call";
047
048    /** Stack of methods. */
049    private final Deque<MethodNode> methodStack = new LinkedList<>();
050
051    /**
052     * Returns the name of the overriding method.
053     * @return the name of the overriding method.
054     */
055    protected abstract String getMethodName();
056
057    @Override
058    public int[] getAcceptableTokens() {
059        return getRequiredTokens();
060    }
061
062    @Override
063    public int[] getDefaultTokens() {
064        return getRequiredTokens();
065    }
066
067    @Override
068    public int[] getRequiredTokens() {
069        return new int[] {
070            TokenTypes.METHOD_DEF,
071            TokenTypes.LITERAL_SUPER,
072        };
073    }
074
075    @Override
076    public void beginTree(DetailAST rootAST) {
077        methodStack.clear();
078    }
079
080    @Override
081    public void visitToken(DetailAST ast) {
082        if (isOverridingMethod(ast)) {
083            methodStack.add(new MethodNode(ast));
084        }
085        else if (isSuperCall(ast)) {
086            final MethodNode methodNode = methodStack.getLast();
087            methodNode.setCallingSuper();
088        }
089    }
090
091    /**
092     * Determines whether a 'super' literal is a call to the super method
093     * for this check.
094     * @param literalSuperAst the AST node of a 'super' literal.
095     * @return true if ast is a call to the super method for this check.
096     */
097    private boolean isSuperCall(DetailAST literalSuperAst) {
098        boolean superCall = false;
099
100        if (literalSuperAst.getType() == TokenTypes.LITERAL_SUPER) {
101            // dot operator?
102            final DetailAST dotAst = literalSuperAst.getParent();
103
104            if (!isSameNameMethod(literalSuperAst)
105                && !hasArguments(dotAst)) {
106                superCall = isSuperCallInOverridingMethod(dotAst);
107            }
108        }
109        return superCall;
110    }
111
112    /**
113     * Determines whether a super call in overriding method.
114     *
115     * @param ast The AST node of a 'dot operator' in 'super' call.
116     * @return true if super call in overriding method.
117     */
118    private boolean isSuperCallInOverridingMethod(DetailAST ast) {
119        boolean inOverridingMethod = false;
120        DetailAST dotAst = ast;
121
122        while (dotAst.getType() != TokenTypes.CTOR_DEF
123                && dotAst.getType() != TokenTypes.INSTANCE_INIT) {
124            if (dotAst.getType() == TokenTypes.METHOD_DEF) {
125                inOverridingMethod = isOverridingMethod(dotAst);
126                break;
127            }
128            dotAst = dotAst.getParent();
129        }
130        return inOverridingMethod;
131    }
132
133    /**
134     * Does method have any arguments.
135     * @param methodCallDotAst DOT DetailAST
136     * @return true if any parameters found
137     */
138    private static boolean hasArguments(DetailAST methodCallDotAst) {
139        final DetailAST argumentsList = methodCallDotAst.getNextSibling();
140        return argumentsList.getChildCount() > 0;
141    }
142
143    /**
144     * Is same name of method.
145     * @param ast method AST
146     * @return true if method name is the same
147     */
148    private boolean isSameNameMethod(DetailAST ast) {
149        AST sibling = ast.getNextSibling();
150        // ignore type parameters
151        if (sibling != null
152            && sibling.getType() == TokenTypes.TYPE_ARGUMENTS) {
153            sibling = sibling.getNextSibling();
154        }
155        return sibling == null || !getMethodName().equals(sibling.getText());
156    }
157
158    @Override
159    public void leaveToken(DetailAST ast) {
160        if (isOverridingMethod(ast)) {
161            final MethodNode methodNode =
162                methodStack.removeLast();
163            if (!methodNode.isCallingSuper()) {
164                final DetailAST methodAST = methodNode.getMethod();
165                final DetailAST nameAST =
166                    methodAST.findFirstToken(TokenTypes.IDENT);
167                log(nameAST, MSG_KEY, nameAST.getText());
168            }
169        }
170    }
171
172    /**
173     * Determines whether an AST is a method definition for this check,
174     * with 0 parameters.
175     * @param ast the method definition AST.
176     * @return true if the method of ast is a method for this check.
177     */
178    private boolean isOverridingMethod(DetailAST ast) {
179        boolean overridingMethod = false;
180
181        if (ast.getType() == TokenTypes.METHOD_DEF
182                && !ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) {
183            final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
184            final String name = nameAST.getText();
185            final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS);
186
187            if (getMethodName().equals(name)
188                    && modifiersAST.findFirstToken(TokenTypes.LITERAL_NATIVE) == null) {
189                final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
190                overridingMethod = params.getChildCount() == 0;
191            }
192        }
193        return overridingMethod;
194    }
195
196    /**
197     * Stack node for a method definition and a record of
198     * whether the method has a call to the super method.
199     */
200    private static class MethodNode {
201
202        /** Method definition. */
203        private final DetailAST method;
204
205        /** True if the overriding method calls the super method. */
206        private boolean callingSuper;
207
208        /**
209         * Constructs a stack node for a method definition.
210         * @param ast AST for the method definition.
211         */
212        MethodNode(DetailAST ast) {
213            method = ast;
214            callingSuper = false;
215        }
216
217        /**
218         * Records that the overriding method has a call to the super method.
219         */
220        public void setCallingSuper() {
221            callingSuper = true;
222        }
223
224        /**
225         * Determines whether the overriding method has a call to the super
226         * method.
227         * @return true if the overriding method has a call to the super method.
228         */
229        public boolean isCallingSuper() {
230            return callingSuper;
231        }
232
233        /**
234         * Returns the overriding method definition AST.
235         * @return the overriding method definition AST.
236         */
237        public DetailAST getMethod() {
238            return method;
239        }
240
241    }
242
243}