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