1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2024 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 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
27
28 /**
29 * <div>
30 * Checks for over-complicated boolean return or yield statements.
31 * For example the following code
32 * </div>
33 * <pre>
34 * if (valid())
35 * return false;
36 * else
37 * return true;
38 * </pre>
39 *
40 * <p>
41 * could be written as
42 * </p>
43 * <pre>
44 * return !valid();
45 * </pre>
46 *
47 * <p>
48 * The idea for this Check has been shamelessly stolen from the equivalent
49 * <a href="https://pmd.github.io/pmd/pmd_rules_java_design.html#simplifybooleanreturns">
50 * PMD</a> rule.
51 * </p>
52 *
53 * <p>
54 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
55 * </p>
56 *
57 * <p>
58 * Violation Message Keys:
59 * </p>
60 * <ul>
61 * <li>
62 * {@code simplify.boolReturn}
63 * </li>
64 * </ul>
65 *
66 * @since 3.0
67 */
68 @StatelessCheck
69 public class SimplifyBooleanReturnCheck
70 extends AbstractCheck {
71
72 /**
73 * A key is pointing to the warning message text in "messages.properties"
74 * file.
75 */
76 public static final String MSG_KEY = "simplify.boolReturn";
77
78 @Override
79 public int[] getAcceptableTokens() {
80 return getRequiredTokens();
81 }
82
83 @Override
84 public int[] getDefaultTokens() {
85 return getRequiredTokens();
86 }
87
88 @Override
89 public int[] getRequiredTokens() {
90 return new int[] {TokenTypes.LITERAL_IF};
91 }
92
93 @Override
94 public void visitToken(DetailAST ast) {
95 // LITERAL_IF has the following four or five children:
96 // '('
97 // condition
98 // ')'
99 // thenStatement
100 // [ LITERAL_ELSE (with the elseStatement as a child) ]
101
102 // don't bother if this is not if then else
103 final DetailAST elseLiteral =
104 ast.findFirstToken(TokenTypes.LITERAL_ELSE);
105 if (elseLiteral != null) {
106 final DetailAST elseStatement = elseLiteral.getFirstChild();
107
108 // skip '(' and ')'
109 final DetailAST condition = ast.getFirstChild().getNextSibling();
110 final DetailAST thenStatement = condition.getNextSibling().getNextSibling();
111
112 if (canReturnOrYieldOnlyBooleanLiteral(thenStatement)
113 && canReturnOrYieldOnlyBooleanLiteral(elseStatement)) {
114 log(ast, MSG_KEY);
115 }
116 }
117 }
118
119 /**
120 * Returns if an AST is a return or a yield statement with a boolean literal
121 * or a compound statement that contains only such a return or a yield statement.
122 *
123 * <p>Returns {@code true} iff ast represents
124 * <pre>
125 * return/yield true/false;
126 * </pre>
127 * or
128 * <pre>
129 * {
130 * return/yield true/false;
131 * }
132 * </pre>
133 *
134 * @param ast the syntax tree to check
135 * @return if ast is a return or a yield statement with a boolean literal.
136 */
137 private static boolean canReturnOrYieldOnlyBooleanLiteral(DetailAST ast) {
138 boolean result = true;
139 if (!isBooleanLiteralReturnOrYieldStatement(ast)) {
140 final DetailAST firstStatement = ast.getFirstChild();
141 result = isBooleanLiteralReturnOrYieldStatement(firstStatement);
142 }
143 return result;
144 }
145
146 /**
147 * Returns if an AST is a return or a yield statement with a boolean literal.
148 *
149 * <p>Returns {@code true} iff ast represents
150 * <pre>
151 * return/yield true/false;
152 * </pre>
153 *
154 * @param ast the syntax tree to check
155 * @return if ast is a return or a yield statement with a boolean literal.
156 */
157 private static boolean isBooleanLiteralReturnOrYieldStatement(DetailAST ast) {
158 boolean booleanReturnStatement = false;
159
160 if (ast != null && (ast.getType() == TokenTypes.LITERAL_RETURN
161 || ast.getType() == TokenTypes.LITERAL_YIELD)) {
162 final DetailAST expr = ast.getFirstChild();
163
164 if (expr.getType() != TokenTypes.SEMI) {
165 final DetailAST value = expr.getFirstChild();
166 booleanReturnStatement = TokenUtil.isBooleanLiteralType(value.getType());
167 }
168 }
169 return booleanReturnStatement;
170 }
171 }