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  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import antlr.collections.AST;
23  import com.puppycrawl.tools.checkstyle.api.Check;
24  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
25  import com.puppycrawl.tools.checkstyle.api.DetailAST;
26  
27  
28  /**
29   * <p>
30   * Checks for overly complicated boolean return statements.
31   * Idea shamelessly stolen from the equivalent PMD rule (pmd.sourceforge.net).
32   * </p>
33   * <p>
34   * An example of how to configure the check is:
35   * </p>
36   * <pre>
37   * &lt;module name="SimplifyBooleanReturn"/&gt;
38   * </pre>
39   * @author Lars Kühne
40   */
41  public class SimplifyBooleanReturnCheck
42      extends Check
43  {
44      @Override
45      public int[] getDefaultTokens()
46      {
47          return new int[] {TokenTypes.LITERAL_IF};
48      }
49  
50      @Override
51      public void visitToken(DetailAST ast)
52      {
53          // LITERAL_IF has the following four or five children:
54          // '('
55          // condition
56          // ')'
57          // thenStatement
58          // [ LITERAL_ELSE (with the elseStatement as a child) ]
59  
60          // don't bother if this is not if then else
61          final AST elseLiteral =
62              ast.findFirstToken(TokenTypes.LITERAL_ELSE);
63          if (elseLiteral == null) {
64              return;
65          }
66          final AST elseStatement = elseLiteral.getFirstChild();
67  
68          // skip '(' and ')'
69          // TODO: Introduce helpers in DetailAST
70          final AST condition = ast.getFirstChild().getNextSibling();
71          final AST thenStatement = condition.getNextSibling().getNextSibling();
72  
73          if (returnsOnlyBooleanLiteral(thenStatement)
74              && returnsOnlyBooleanLiteral(elseStatement))
75          {
76              log(ast.getLineNo(), ast.getColumnNo(), "simplify.boolreturn");
77          }
78      }
79  
80      /**
81       * Returns if an AST is a return statment with a boolean literal
82       * or a compound statement that contains only such a return statement.
83       *
84       * Returns <code>true</code> iff ast represents
85       * <br/>
86       * <pre>
87       * return true/false;
88       * </pre>
89       * or
90       * <br/>
91       * <pre>
92       * {
93       *   return true/false;
94       * }
95       * </pre>
96       *
97       * @param ast the sytax tree to check
98       * @return if ast is a return statment with a boolean literal.
99       */
100     private static boolean returnsOnlyBooleanLiteral(AST ast)
101     {
102         if (isBooleanLiteralReturnStatement(ast)) {
103             return true;
104         }
105 
106         final AST firstStmnt = ast.getFirstChild();
107         return isBooleanLiteralReturnStatement(firstStmnt);
108     }
109 
110     /**
111      * Returns if an AST is a return statment with a boolean literal.
112      *
113      * Returns <code>true</code> iff ast represents
114      * <br/>
115      * <pre>
116      * return true/false;
117      * </pre>
118      *
119      * @param ast the sytax tree to check
120      * @return if ast is a return statment with a boolean literal.
121      */
122     private static boolean isBooleanLiteralReturnStatement(AST ast)
123     {
124         if ((ast == null) || (ast.getType() != TokenTypes.LITERAL_RETURN)) {
125             return false;
126         }
127 
128         final AST expr = ast.getFirstChild();
129 
130         if ((expr == null) || (expr.getType() == TokenTypes.SEMI)) {
131             return false;
132         }
133 
134         final AST value = expr.getFirstChild();
135         return isBooleanLiteralType(value.getType());
136     }
137 
138     /**
139      * Checks if a token type is a literal true or false.
140      * @param tokenType the TokenType
141      * @return true iff tokenType is LITERAL_TRUE or LITERAL_FALSE
142      */
143     private static boolean isBooleanLiteralType(final int tokenType)
144     {
145         final boolean iastrue = (tokenType == TokenTypes.LITERAL_TRUE);
146         final boolean isFalse = (tokenType == TokenTypes.LITERAL_FALSE);
147         return iastrue || isFalse;
148     }
149 }