001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 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.api.AbstractCheck; 027import com.puppycrawl.tools.checkstyle.api.DetailAST; 028import com.puppycrawl.tools.checkstyle.api.TokenTypes; 029import com.puppycrawl.tools.checkstyle.utils.ScopeUtils; 030 031/** 032 * <p> 033 * Abstract class for checking that an overriding method with no parameters 034 * invokes the super method. 035 * </p> 036 * @author Rick Giles 037 */ 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 new int[] { 059 TokenTypes.METHOD_DEF, 060 TokenTypes.LITERAL_SUPER, 061 }; 062 } 063 064 @Override 065 public int[] getDefaultTokens() { 066 return getAcceptableTokens(); 067 } 068 069 @Override 070 public int[] getRequiredTokens() { 071 return getDefaultTokens(); 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 124 if (dotAst.getType() == TokenTypes.METHOD_DEF) { 125 inOverridingMethod = isOverridingMethod(dotAst); 126 break; 127 } 128 dotAst = dotAst.getParent(); 129 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 151 AST sibling = ast.getNextSibling(); 152 // ignore type parameters 153 if (sibling != null 154 && sibling.getType() == TokenTypes.TYPE_ARGUMENTS) { 155 sibling = sibling.getNextSibling(); 156 } 157 if (sibling == null) { 158 return true; 159 } 160 final String name = sibling.getText(); 161 return !getMethodName().equals(name); 162 } 163 164 @Override 165 public void leaveToken(DetailAST ast) { 166 if (isOverridingMethod(ast)) { 167 final MethodNode methodNode = 168 methodStack.removeLast(); 169 if (!methodNode.isCallingSuper()) { 170 final DetailAST methodAST = methodNode.getMethod(); 171 final DetailAST nameAST = 172 methodAST.findFirstToken(TokenTypes.IDENT); 173 log(nameAST.getLineNo(), nameAST.getColumnNo(), 174 MSG_KEY, nameAST.getText()); 175 } 176 } 177 } 178 179 /** 180 * Determines whether an AST is a method definition for this check, 181 * with 0 parameters. 182 * @param ast the method definition AST. 183 * @return true if the method of ast is a method for this check. 184 */ 185 private boolean isOverridingMethod(DetailAST ast) { 186 boolean overridingMethod = false; 187 188 if (ast.getType() == TokenTypes.METHOD_DEF 189 && !ScopeUtils.isInInterfaceOrAnnotationBlock(ast)) { 190 final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT); 191 final String name = nameAST.getText(); 192 final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS); 193 194 if (getMethodName().equals(name) 195 && !modifiersAST.branchContains(TokenTypes.LITERAL_NATIVE)) { 196 final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS); 197 overridingMethod = params.getChildCount() == 0; 198 } 199 } 200 return overridingMethod; 201 } 202 203 /** 204 * Stack node for a method definition and a record of 205 * whether the method has a call to the super method. 206 * @author Rick Giles 207 */ 208 private static class MethodNode { 209 /** Method definition. */ 210 private final DetailAST method; 211 212 /** True if the overriding method calls the super method. */ 213 private boolean callingSuper; 214 215 /** 216 * Constructs a stack node for a method definition. 217 * @param ast AST for the method definition. 218 */ 219 MethodNode(DetailAST ast) { 220 method = ast; 221 callingSuper = false; 222 } 223 224 /** 225 * Records that the overriding method has a call to the super method. 226 */ 227 public void setCallingSuper() { 228 callingSuper = true; 229 } 230 231 /** 232 * Determines whether the overriding method has a call to the super 233 * method. 234 * @return true if the overriding method has a call to the super method. 235 */ 236 public boolean isCallingSuper() { 237 return callingSuper; 238 } 239 240 /** 241 * Returns the overriding method definition AST. 242 * @return the overriding method definition AST. 243 */ 244 public DetailAST getMethod() { 245 return method; 246 } 247 } 248}