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