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