1 ////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code for adherence to a set of rules.
3 // Copyright (C) 2001-2019 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 com.puppycrawl.tools.checkstyle.StatelessCheck;
23 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24 import com.puppycrawl.tools.checkstyle.api.DetailAST;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26
27 /**
28 * <p>
29 * Checks for overly complicated boolean return statements.
30 * Idea shamelessly stolen from the equivalent PMD rule (pmd.sourceforge.net).
31 * </p>
32 * <p>
33 * An example of how to configure the check is:
34 * </p>
35 * <pre>
36 * <module name="SimplifyBooleanReturn"/>
37 * </pre>
38 */
39 @StatelessCheck
40 public class SimplifyBooleanReturnCheck
41 extends AbstractCheck {
42
43 /**
44 * A key is pointing to the warning message text in "messages.properties"
45 * file.
46 */
47 public static final String MSG_KEY = "simplify.boolReturn";
48
49 @Override
50 public int[] getAcceptableTokens() {
51 return getRequiredTokens();
52 }
53
54 @Override
55 public int[] getDefaultTokens() {
56 return getRequiredTokens();
57 }
58
59 @Override
60 public int[] getRequiredTokens() {
61 return new int[] {TokenTypes.LITERAL_IF};
62 }
63
64 @Override
65 public void visitToken(DetailAST ast) {
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 DetailAST elseLiteral =
75 ast.findFirstToken(TokenTypes.LITERAL_ELSE);
76 if (elseLiteral != null) {
77 final DetailAST elseStatement = elseLiteral.getFirstChild();
78
79 // skip '(' and ')'
80 final DetailAST condition = ast.getFirstChild().getNextSibling();
81 final DetailAST thenStatement = condition.getNextSibling().getNextSibling();
82
83 if (canReturnOnlyBooleanLiteral(thenStatement)
84 && canReturnOnlyBooleanLiteral(elseStatement)) {
85 log(ast, MSG_KEY);
86 }
87 }
88 }
89
90 /**
91 * Returns if an AST is a return statement with a boolean literal
92 * or a compound statement that contains only such a return statement.
93 *
94 * <p>Returns {@code true} iff ast represents
95 * <br/>
96 * <pre>
97 * return true/false;
98 * </pre>
99 * or
100 * <br/>
101 * <pre>
102 * {
103 * return true/false;
104 * }
105 * </pre>
106 *
107 * @param ast the syntax tree to check
108 * @return if ast is a return statement with a boolean literal.
109 */
110 private static boolean canReturnOnlyBooleanLiteral(DetailAST ast) {
111 boolean result = true;
112 if (!isBooleanLiteralReturnStatement(ast)) {
113 final DetailAST firstStatement = ast.getFirstChild();
114 result = isBooleanLiteralReturnStatement(firstStatement);
115 }
116 return result;
117 }
118
119 /**
120 * Returns if an AST is a return statement with a boolean literal.
121 *
122 * <p>Returns {@code true} iff ast represents
123 * <br/>
124 * <pre>
125 * return true/false;
126 * </pre>
127 *
128 * @param ast the syntax tree to check
129 * @return if ast is a return statement with a boolean literal.
130 */
131 private static boolean isBooleanLiteralReturnStatement(DetailAST ast) {
132 boolean booleanReturnStatement = false;
133
134 if (ast != null && ast.getType() == TokenTypes.LITERAL_RETURN) {
135 final DetailAST expr = ast.getFirstChild();
136
137 if (expr.getType() != TokenTypes.SEMI) {
138 final DetailAST value = expr.getFirstChild();
139 booleanReturnStatement = isBooleanLiteralType(value.getType());
140 }
141 }
142 return booleanReturnStatement;
143 }
144
145 /**
146 * Checks if a token type is a literal true or false.
147 * @param tokenType the TokenType
148 * @return true iff tokenType is LITERAL_TRUE or LITERAL_FALSE
149 */
150 private static boolean isBooleanLiteralType(final int tokenType) {
151 final boolean isTrue = tokenType == TokenTypes.LITERAL_TRUE;
152 final boolean isFalse = tokenType == TokenTypes.LITERAL_FALSE;
153 return isTrue || isFalse;
154 }
155
156 }