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}