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