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 * <module name="SimplifyBooleanReturn"/>
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 }