View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2014  Oliver Burn
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  package com.puppycrawl.tools.checkstyle.checks.coding;
20  
21  import com.puppycrawl.tools.checkstyle.api.DetailAST;
22  import com.puppycrawl.tools.checkstyle.api.FastStack;
23  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
24  import com.puppycrawl.tools.checkstyle.checks.AbstractFormatCheck;
25  
26  /**
27   * <p>
28   * Restricts return statements to a specified count (default = 2).
29   * Ignores specified methods (<code>equals()</code> by default).
30   * </p>
31   * <p>
32   * Rationale: Too many return points can be indication that code is
33   * attempting to do too much or may be difficult to understand.
34   * </p>
35   *
36   * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
37   * TODO: Test for inside a static block
38   */
39  public final class ReturnCountCheck extends AbstractFormatCheck
40  {
41      /** Default allowed number of return statements. */
42      private static final int DEFAULT_MAX = 2;
43  
44      /** Stack of method contexts. */
45      private final FastStack<Context> contextStack = FastStack.newInstance();
46      /** Maximum allowed number of return stmts. */
47      private int max;
48      /** Current method context. */
49      private Context context;
50  
51      /** Creates new instance of the checks. */
52      public ReturnCountCheck()
53      {
54          super("^equals$");
55          setMax(DEFAULT_MAX);
56      }
57  
58      @Override
59      public int[] getDefaultTokens()
60      {
61          return new int[] {
62              TokenTypes.CTOR_DEF,
63              TokenTypes.METHOD_DEF,
64              TokenTypes.LITERAL_RETURN,
65          };
66      }
67  
68      @Override
69      public int[] getRequiredTokens()
70      {
71          return new int[]{
72              TokenTypes.CTOR_DEF,
73              TokenTypes.METHOD_DEF,
74          };
75      }
76  
77      /**
78       * Getter for max property.
79       * @return maximum allowed number of return statements.
80       */
81      public int getMax()
82      {
83          return max;
84      }
85  
86      /**
87       * Setter for max property.
88       * @param max maximum allowed number of return statements.
89       */
90      public void setMax(int max)
91      {
92          this.max = max;
93      }
94  
95      @Override
96      public void beginTree(DetailAST rootAST)
97      {
98          context = null;
99          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 }