View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2015 the original author or authors.
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  
45      /**
46       * A key is pointing to the warning message text in "messages.properties"
47       * file.
48       */
49      public static final String MSG_KEY = "simplify.boolreturn";
50  
51      @Override
52      public int[] getDefaultTokens()
53      {
54          return new int[] {TokenTypes.LITERAL_IF};
55      }
56  
57      @Override
58      public int[] getAcceptableTokens()
59      {
60          return new int[] {TokenTypes.LITERAL_IF};
61      }
62  
63      @Override
64      public void visitToken(DetailAST ast)
65      {
66          // LITERAL_IF has the following four or five children:
67          // '('
68          // condition
69          // ')'
70          // thenStatement
71          // [ LITERAL_ELSE (with the elseStatement as a child) ]
72  
73          // don't bother if this is not if then else
74          final AST elseLiteral =
75              ast.findFirstToken(TokenTypes.LITERAL_ELSE);
76          if (elseLiteral == null) {
77              return;
78          }
79          final AST elseStatement = elseLiteral.getFirstChild();
80  
81          // skip '(' and ')'
82          // TODO: Introduce helpers in DetailAST
83          final AST condition = ast.getFirstChild().getNextSibling();
84          final AST thenStatement = condition.getNextSibling().getNextSibling();
85  
86          if (returnsOnlyBooleanLiteral(thenStatement)
87              && returnsOnlyBooleanLiteral(elseStatement))
88          {
89              log(ast.getLineNo(), ast.getColumnNo(), MSG_KEY);
90          }
91      }
92  
93      /**
94       * Returns if an AST is a return statment with a boolean literal
95       * or a compound statement that contains only such a return statement.
96       *
97       * Returns <code>true</code> iff ast represents
98       * <br/>
99       * <pre>
100      * return true/false;
101      * </pre>
102      * or
103      * <br/>
104      * <pre>
105      * {
106      *   return true/false;
107      * }
108      * </pre>
109      *
110      * @param ast the sytax tree to check
111      * @return if ast is a return statment with a boolean literal.
112      */
113     private static boolean returnsOnlyBooleanLiteral(AST ast)
114     {
115         if (isBooleanLiteralReturnStatement(ast)) {
116             return true;
117         }
118 
119         final AST firstStmnt = ast.getFirstChild();
120         return isBooleanLiteralReturnStatement(firstStmnt);
121     }
122 
123     /**
124      * Returns if an AST is a return statment with a boolean literal.
125      *
126      * Returns <code>true</code> iff ast represents
127      * <br/>
128      * <pre>
129      * return true/false;
130      * </pre>
131      *
132      * @param ast the sytax tree to check
133      * @return if ast is a return statment with a boolean literal.
134      */
135     private static boolean isBooleanLiteralReturnStatement(AST ast)
136     {
137         if ((ast == null) || (ast.getType() != TokenTypes.LITERAL_RETURN)) {
138             return false;
139         }
140 
141         final AST expr = ast.getFirstChild();
142 
143         if ((expr == null) || (expr.getType() == TokenTypes.SEMI)) {
144             return false;
145         }
146 
147         final AST value = expr.getFirstChild();
148         return isBooleanLiteralType(value.getType());
149     }
150 
151     /**
152      * Checks if a token type is a literal true or false.
153      * @param tokenType the TokenType
154      * @return true iff tokenType is LITERAL_TRUE or LITERAL_FALSE
155      */
156     private static boolean isBooleanLiteralType(final int tokenType)
157     {
158         final boolean iastrue = (tokenType == TokenTypes.LITERAL_TRUE);
159         final boolean isFalse = (tokenType == TokenTypes.LITERAL_FALSE);
160         return iastrue || isFalse;
161     }
162 }