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 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 ArrayDeque<>(); 049 050 /** 051 * Creates a new {@code AbstractSuperCheck} instance. 052 */ 053 protected AbstractSuperCheck() { 054 // no code by default 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 100 * for this check. 101 * 102 * @param literalSuperAst the AST node of a 'super' literal. 103 * @return true if ast is a call to the super method for this check. 104 */ 105 private boolean isSuperCall(DetailAST literalSuperAst) { 106 boolean superCall = false; 107 108 if (!isSameNameMethod(literalSuperAst)) { 109 final DetailAST parent = literalSuperAst.getParent(); 110 if (parent.getType() == TokenTypes.METHOD_REF 111 || !hasArguments(parent)) { 112 superCall = isSuperCallInOverridingMethod(parent); 113 } 114 } 115 return superCall; 116 } 117 118 /** 119 * Determines whether a super call in overriding method. 120 * 121 * @param ast The AST node of a 'dot operator' in 'super' call. 122 * @return true if super call in overriding method. 123 */ 124 private boolean isSuperCallInOverridingMethod(DetailAST ast) { 125 boolean inOverridingMethod = false; 126 DetailAST dotAst = ast; 127 128 while (dotAst.getType() != TokenTypes.CTOR_DEF 129 && dotAst.getType() != TokenTypes.INSTANCE_INIT) { 130 if (dotAst.getType() == TokenTypes.METHOD_DEF) { 131 inOverridingMethod = isOverridingMethod(dotAst); 132 break; 133 } 134 dotAst = dotAst.getParent(); 135 } 136 return inOverridingMethod; 137 } 138 139 /** 140 * Does method have any arguments. 141 * 142 * @param methodCallDotAst DOT DetailAST 143 * @return true if any parameters found 144 */ 145 private static boolean hasArguments(DetailAST methodCallDotAst) { 146 final DetailAST argumentsList = methodCallDotAst.getNextSibling(); 147 return argumentsList.hasChildren(); 148 } 149 150 /** 151 * Is same name of method. 152 * 153 * @param ast method AST 154 * @return true if method name is the same 155 */ 156 private boolean isSameNameMethod(DetailAST ast) { 157 DetailAST sibling = ast.getNextSibling(); 158 // ignore type parameters 159 if (sibling != null 160 && sibling.getType() == TokenTypes.TYPE_ARGUMENTS) { 161 sibling = sibling.getNextSibling(); 162 } 163 return sibling == null || !getMethodName().equals(sibling.getText()); 164 } 165 166 @Override 167 public void leaveToken(DetailAST ast) { 168 if (isOverridingMethod(ast)) { 169 final MethodNode methodNode = 170 methodStack.removeLast(); 171 if (!methodNode.isCallingSuper()) { 172 final DetailAST methodAST = methodNode.getMethod(); 173 final DetailAST nameAST = 174 methodAST.findFirstToken(TokenTypes.IDENT); 175 log(nameAST, MSG_KEY, nameAST.getText()); 176 } 177 } 178 } 179 180 /** 181 * Determines whether an AST is a method definition for this check, 182 * without any parameters. 183 * 184 * @param ast the method definition AST. 185 * @return true if the method of ast is a method for this check. 186 */ 187 private boolean isOverridingMethod(DetailAST ast) { 188 boolean overridingMethod = false; 189 190 if (ast.getType() == TokenTypes.METHOD_DEF 191 && !ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) { 192 final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT); 193 final String name = nameAST.getText(); 194 final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS); 195 196 if (getMethodName().equals(name) 197 && modifiersAST.findFirstToken(TokenTypes.LITERAL_NATIVE) == null) { 198 final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS); 199 overridingMethod = !params.hasChildren(); 200 } 201 } 202 return overridingMethod; 203 } 204 205 /** 206 * Stack node for a method definition and a record of 207 * whether the method has a call to the super method. 208 */ 209 private static final class MethodNode { 210 211 /** Method definition. */ 212 private final DetailAST method; 213 214 /** True if the overriding method calls the super method. */ 215 private boolean callingSuper; 216 217 /** 218 * Constructs a stack node for a method definition. 219 * 220 * @param ast AST for the method definition. 221 */ 222 private MethodNode(DetailAST ast) { 223 method = ast; 224 } 225 226 /** 227 * Records that the overriding method has a call to the super method. 228 */ 229 /* package */ void setCallingSuper() { 230 callingSuper = true; 231 } 232 233 /** 234 * Determines whether the overriding method has a call to the super 235 * method. 236 * 237 * @return true if the overriding method has a call to the super method. 238 */ 239 /* package */ boolean isCallingSuper() { 240 return callingSuper; 241 } 242 243 /** 244 * Returns the overriding method definition AST. 245 * 246 * @return the overriding method definition AST. 247 */ 248 /* package */ DetailAST getMethod() { 249 return method; 250 } 251 252 } 253 254}