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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.coding; 020 021import com.puppycrawl.tools.checkstyle.api.DetailAST; 022import com.puppycrawl.tools.checkstyle.api.FastStack; 023import com.puppycrawl.tools.checkstyle.api.TokenTypes; 024import com.puppycrawl.tools.checkstyle.checks.AbstractFormatCheck; 025 026/** 027 * <p> 028 * Restricts return statements to a specified count (default = 2). 029 * Ignores specified methods (<code>equals()</code> by default). 030 * </p> 031 * <p> 032 * Rationale: Too many return points can be indication that code is 033 * attempting to do too much or may be difficult to understand. 034 * </p> 035 * 036 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> 037 * TODO: Test for inside a static block 038 */ 039public final class ReturnCountCheck extends AbstractFormatCheck 040{ 041 /** Default allowed number of return statements. */ 042 private static final int DEFAULT_MAX = 2; 043 044 /** Stack of method contexts. */ 045 private final FastStack<Context> contextStack = FastStack.newInstance(); 046 /** Maximum allowed number of return stmts. */ 047 private int max; 048 /** Current method context. */ 049 private Context context; 050 051 /** Creates new instance of the checks. */ 052 public ReturnCountCheck() 053 { 054 super("^equals$"); 055 setMax(DEFAULT_MAX); 056 } 057 058 @Override 059 public int[] getDefaultTokens() 060 { 061 return new int[] { 062 TokenTypes.CTOR_DEF, 063 TokenTypes.METHOD_DEF, 064 TokenTypes.LITERAL_RETURN, 065 }; 066 } 067 068 @Override 069 public int[] getRequiredTokens() 070 { 071 return new int[]{ 072 TokenTypes.CTOR_DEF, 073 TokenTypes.METHOD_DEF, 074 }; 075 } 076 077 /** 078 * Getter for max property. 079 * @return maximum allowed number of return statements. 080 */ 081 public int getMax() 082 { 083 return max; 084 } 085 086 /** 087 * Setter for max property. 088 * @param max maximum allowed number of return statements. 089 */ 090 public void setMax(int max) 091 { 092 this.max = max; 093 } 094 095 @Override 096 public void beginTree(DetailAST rootAST) 097 { 098 context = null; 099 contextStack.clear(); 100 } 101 102 @Override 103 public void visitToken(DetailAST ast) 104 { 105 switch (ast.getType()) { 106 case TokenTypes.CTOR_DEF: 107 case TokenTypes.METHOD_DEF: 108 visitMethodDef(ast); 109 break; 110 case TokenTypes.LITERAL_RETURN: 111 context.visitLiteralReturn(); 112 break; 113 default: 114 throw new IllegalStateException(ast.toString()); 115 } 116 } 117 118 @Override 119 public void leaveToken(DetailAST ast) 120 { 121 switch (ast.getType()) { 122 case TokenTypes.CTOR_DEF: 123 case TokenTypes.METHOD_DEF: 124 leaveMethodDef(ast); 125 break; 126 case TokenTypes.LITERAL_RETURN: 127 // Do nothing 128 break; 129 default: 130 throw new IllegalStateException(ast.toString()); 131 } 132 } 133 134 /** 135 * Creates new method context and places old one on the stack. 136 * @param ast method definition for check. 137 */ 138 private void visitMethodDef(DetailAST ast) 139 { 140 contextStack.push(context); 141 final DetailAST methodNameAST = ast.findFirstToken(TokenTypes.IDENT); 142 context = 143 new Context(!getRegexp().matcher(methodNameAST.getText()).find()); 144 } 145 146 /** 147 * Checks number of return statements and restore 148 * previous method context. 149 * @param ast method def node. 150 */ 151 private void leaveMethodDef(DetailAST ast) 152 { 153 context.checkCount(ast); 154 context = contextStack.pop(); 155 } 156 157 /** 158 * Class to encapsulate information about one method. 159 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> 160 */ 161 private class Context 162 { 163 /** Whether we should check this method or not. */ 164 private final boolean checking; 165 /** Counter for return statements. */ 166 private int count; 167 168 /** 169 * Creates new method context. 170 * @param checking should we check this method or not. 171 */ 172 public Context(boolean checking) 173 { 174 this.checking = checking; 175 count = 0; 176 } 177 178 /** Increase number of return statements. */ 179 public void visitLiteralReturn() 180 { 181 ++count; 182 } 183 184 /** 185 * Checks if number of return statements in method more 186 * than allowed. 187 * @param ast method def associated with this context. 188 */ 189 public void checkCount(DetailAST ast) 190 { 191 if (checking && (count > getMax())) { 192 log(ast.getLineNo(), ast.getColumnNo(), "return.count", 193 count, getMax()); 194 } 195 } 196 } 197}