001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2015 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; 023 024import antlr.collections.AST; 025 026import com.google.common.collect.Lists; 027import com.puppycrawl.tools.checkstyle.api.Check; 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 */ 039public abstract class AbstractSuperCheck 040 extends Check { 041 042 /** 043 * A key is pointing to the warning message text in "messages.properties" 044 * file. 045 */ 046 public static final String MSG_KEY = "missing.super.call"; 047 048 /** Stack of methods. */ 049 private final Deque<MethodNode> methodStack = Lists.newLinkedList(); 050 051 @Override 052 public int[] getAcceptableTokens() { 053 return new int[] { 054 TokenTypes.METHOD_DEF, 055 TokenTypes.LITERAL_SUPER, 056 }; 057 } 058 059 @Override 060 public int[] getDefaultTokens() { 061 return getAcceptableTokens(); 062 } 063 064 @Override 065 public int[] getRequiredTokens() { 066 return getDefaultTokens(); 067 } 068 069 /** 070 * Returns the name of the overriding method. 071 * @return the name of the overriding method. 072 */ 073 protected abstract String getMethodName(); 074 075 @Override 076 public void beginTree(DetailAST rootAST) { 077 methodStack.clear(); 078 } 079 080 @Override 081 public void visitToken(DetailAST ast) { 082 if (isOverridingMethod(ast)) { 083 methodStack.add(new MethodNode(ast)); 084 } 085 else if (isSuperCall(ast)) { 086 final MethodNode methodNode = methodStack.getLast(); 087 methodNode.setCallingSuper(); 088 } 089 } 090 091 /** 092 * Determines whether a 'super' literal is a call to the super method 093 * for this check. 094 * @param literalSuperAst the AST node of a 'super' literal. 095 * @return true if ast is a call to the super method for this check. 096 */ 097 private boolean isSuperCall(DetailAST literalSuperAst) { 098 boolean superCall = false; 099 100 if (literalSuperAst.getType() == TokenTypes.LITERAL_SUPER) { 101 // dot operator? 102 final DetailAST dotAst = literalSuperAst.getParent(); 103 104 if (dotAst.getType() == TokenTypes.DOT 105 && !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 != null 124 && dotAst.getType() != TokenTypes.CTOR_DEF 125 && dotAst.getType() != TokenTypes.INSTANCE_INIT) { 126 127 if (dotAst.getType() == TokenTypes.METHOD_DEF) { 128 inOverridingMethod = isOverridingMethod(dotAst); 129 break; 130 } 131 dotAst = dotAst.getParent(); 132 133 } 134 return inOverridingMethod; 135 } 136 137 /** 138 * Does method have any arguments. 139 * @param methodCallDotAst DOT DetailAST 140 * @return true if any parameters found 141 */ 142 private static boolean hasArguments(DetailAST methodCallDotAst) { 143 final DetailAST argumentsList = methodCallDotAst.getNextSibling(); 144 return argumentsList.getChildCount() > 0; 145 } 146 147 /** 148 * Is same name of method. 149 * @param ast method AST 150 * @return true if method name is the same 151 */ 152 private boolean isSameNameMethod(DetailAST ast) { 153 154 AST sibling = ast.getNextSibling(); 155 // ignore type parameters 156 if (sibling != null 157 && sibling.getType() == TokenTypes.TYPE_ARGUMENTS) { 158 sibling = sibling.getNextSibling(); 159 } 160 if (sibling == null || sibling.getType() != TokenTypes.IDENT) { 161 return true; 162 } 163 final String name = sibling.getText(); 164 return !getMethodName().equals(name); 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.getLineNo(), nameAST.getColumnNo(), 177 MSG_KEY, nameAST.getText()); 178 } 179 } 180 } 181 182 /** 183 * Determines whether an AST is a method definition for this check, 184 * with 0 parameters. 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 && !ScopeUtils.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.branchContains(TokenTypes.LITERAL_NATIVE)) { 199 final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS); 200 overridingMethod = params.getChildCount() == 0; 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 * @author Rick Giles 210 */ 211 private static class MethodNode { 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 * @param ast AST for the method definition. 221 */ 222 MethodNode(DetailAST ast) { 223 method = ast; 224 callingSuper = false; 225 } 226 227 /** 228 * Records that the overriding method has a call to the super method. 229 */ 230 public void setCallingSuper() { 231 callingSuper = true; 232 } 233 234 /** 235 * Determines whether the overriding method has a call to the super 236 * method. 237 * @return true if the overriding method has a call to the super method. 238 */ 239 public boolean isCallingSuper() { 240 return callingSuper; 241 } 242 243 /** 244 * Returns the overriding method definition AST. 245 * @return the overriding method definition AST. 246 */ 247 public DetailAST getMethod() { 248 return method; 249 } 250 } 251}