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