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.ScopeUtils;
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.getLineNo(), nameAST.getColumnNo(),
168                    MSG_KEY, nameAST.getText());
169            }
170        }
171    }
172
173    /**
174     * Determines whether an AST is a method definition for this check,
175     * with 0 parameters.
176     * @param ast the method definition AST.
177     * @return true if the method of ast is a method for this check.
178     */
179    private boolean isOverridingMethod(DetailAST ast) {
180        boolean overridingMethod = false;
181
182        if (ast.getType() == TokenTypes.METHOD_DEF
183                && !ScopeUtils.isInInterfaceOrAnnotationBlock(ast)) {
184            final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
185            final String name = nameAST.getText();
186            final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS);
187
188            if (getMethodName().equals(name)
189                    && modifiersAST.findFirstToken(TokenTypes.LITERAL_NATIVE) == null) {
190                final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
191                overridingMethod = params.getChildCount() == 0;
192            }
193        }
194        return overridingMethod;
195    }
196
197    /**
198     * Stack node for a method definition and a record of
199     * whether the method has a call to the super method.
200     */
201    private static class MethodNode {
202
203        /** Method definition. */
204        private final DetailAST method;
205
206        /** True if the overriding method calls the super method. */
207        private boolean callingSuper;
208
209        /**
210         * Constructs a stack node for a method definition.
211         * @param ast AST for the method definition.
212         */
213        MethodNode(DetailAST ast) {
214            method = ast;
215            callingSuper = false;
216        }
217
218        /**
219         * Records that the overriding method has a call to the super method.
220         */
221        public void setCallingSuper() {
222            callingSuper = true;
223        }
224
225        /**
226         * Determines whether the overriding method has a call to the super
227         * method.
228         * @return true if the overriding method has a call to the super method.
229         */
230        public boolean isCallingSuper() {
231            return callingSuper;
232        }
233
234        /**
235         * Returns the overriding method definition AST.
236         * @return the overriding method definition AST.
237         */
238        public DetailAST getMethod() {
239            return method;
240        }
241
242    }
243
244}