001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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 antlr.collections.AST; 023import com.google.common.collect.Lists; 024import com.puppycrawl.tools.checkstyle.api.Check; 025import com.puppycrawl.tools.checkstyle.api.DetailAST; 026import com.puppycrawl.tools.checkstyle.api.ScopeUtils; 027import com.puppycrawl.tools.checkstyle.api.TokenTypes; 028import java.util.LinkedList; 029 030/** 031 * <p> 032 * Abstract class for checking that an overriding method with no parameters 033 * invokes the super method. 034 * </p> 035 * @author Rick Giles 036 */ 037public abstract class AbstractSuperCheck 038 extends Check 039{ 040 /** 041 * Stack node for a method definition and a record of 042 * whether the method has a call to the super method. 043 * @author Rick Giles 044 */ 045 private static class MethodNode 046 { 047 /** method definition */ 048 private final DetailAST method; 049 050 /** true if the overriding method calls the super method */ 051 private boolean callsSuper; 052 053 /** 054 * Constructs a stack node for a method definition. 055 * @param ast AST for the method definition. 056 */ 057 public MethodNode(DetailAST ast) 058 { 059 method = ast; 060 callsSuper = false; 061 } 062 063 /** 064 * Records that the overriding method has a call to the super method. 065 */ 066 public void setCallsSuper() 067 { 068 callsSuper = true; 069 } 070 071 /** 072 * Determines whether the overriding method has a call to the super 073 * method. 074 * @return true if the overriding method has a call to the super 075 * method. 076 */ 077 public boolean getCallsSuper() 078 { 079 return callsSuper; 080 } 081 082 /** 083 * Returns the overriding method definition AST. 084 * @return the overriding method definition AST. 085 */ 086 public DetailAST getMethod() 087 { 088 return method; 089 } 090 } 091 092 /** stack of methods */ 093 private final LinkedList<MethodNode> methodStack = Lists.newLinkedList(); 094 095 @Override 096 public int[] getDefaultTokens() 097 { 098 return new int[] { 099 TokenTypes.METHOD_DEF, 100 TokenTypes.LITERAL_SUPER, 101 }; 102 } 103 104 /** 105 * Returns the name of the overriding method. 106 * @return the name of the overriding method. 107 */ 108 protected abstract String getMethodName(); 109 110 @Override 111 public void beginTree(DetailAST rootAST) 112 { 113 methodStack.clear(); 114 } 115 116 @Override 117 public void visitToken(DetailAST ast) 118 { 119 if (isOverridingMethod(ast)) { 120 methodStack.add(new MethodNode(ast)); 121 } 122 else if (isSuperCall(ast)) { 123 final MethodNode methodNode = methodStack.getLast(); 124 methodNode.setCallsSuper(); 125 } 126 } 127 128 /** 129 * Determines whether a 'super' literal is a call to the super method 130 * for this check. 131 * @param ast the AST node of a 'super' literal. 132 * @return true if ast is a call to the super method 133 * for this check. 134 */ 135 private boolean isSuperCall(DetailAST ast) 136 { 137 if (ast.getType() != TokenTypes.LITERAL_SUPER) { 138 return false; 139 } 140 // dot operator? 141 DetailAST parent = ast.getParent(); 142 if ((parent == null) || (parent.getType() != TokenTypes.DOT)) { 143 return false; 144 } 145 146 // same name of method 147 AST sibling = ast.getNextSibling(); 148 // ignore type parameters 149 if ((sibling != null) 150 && (sibling.getType() == TokenTypes.TYPE_ARGUMENTS)) 151 { 152 sibling = sibling.getNextSibling(); 153 } 154 if ((sibling == null) || (sibling.getType() != TokenTypes.IDENT)) { 155 return false; 156 } 157 final String name = sibling.getText(); 158 if (!getMethodName().equals(name)) { 159 return false; 160 } 161 162 // 0 parameters? 163 final DetailAST args = parent.getNextSibling(); 164 if ((args == null) || (args.getType() != TokenTypes.ELIST)) { 165 return false; 166 } 167 if (args.getChildCount() != 0) { 168 return false; 169 } 170 171 // in an overriding method for this check? 172 while (parent != null) { 173 if (parent.getType() == TokenTypes.METHOD_DEF) { 174 return isOverridingMethod(parent); 175 } 176 else if ((parent.getType() == TokenTypes.CTOR_DEF) 177 || (parent.getType() == TokenTypes.INSTANCE_INIT)) 178 { 179 return false; 180 } 181 parent = parent.getParent(); 182 } 183 return false; 184 } 185 186 @Override 187 public void leaveToken(DetailAST ast) 188 { 189 if (isOverridingMethod(ast)) { 190 final MethodNode methodNode = 191 methodStack.removeLast(); 192 if (!methodNode.getCallsSuper()) { 193 final DetailAST methodAST = methodNode.getMethod(); 194 final DetailAST nameAST = 195 methodAST.findFirstToken(TokenTypes.IDENT); 196 log(nameAST.getLineNo(), nameAST.getColumnNo(), 197 "missing.super.call", nameAST.getText()); 198 } 199 } 200 } 201 202 /** 203 * Determines whether an AST is a method definition for this check, 204 * with 0 parameters. 205 * @param ast the method definition AST. 206 * @return true if the method of ast is a method for this check. 207 */ 208 private boolean isOverridingMethod(DetailAST ast) 209 { 210 if ((ast.getType() != TokenTypes.METHOD_DEF) 211 || ScopeUtils.inInterfaceOrAnnotationBlock(ast)) 212 { 213 return false; 214 } 215 final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT); 216 final String name = nameAST.getText(); 217 if (!getMethodName().equals(name)) { 218 return false; 219 } 220 final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS); 221 return (params.getChildCount() == 0); 222 } 223}