001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2022 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.coding; 021 022import com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 027 028/** 029 * <p> 030 * Checks for over-complicated boolean return statements. 031 * For example the following code 032 * </p> 033 * <pre> 034 * if (valid()) 035 * return false; 036 * else 037 * return true; 038 * </pre> 039 * <p> 040 * could be written as 041 * </p> 042 * <pre> 043 * return !valid(); 044 * </pre> 045 * <p> 046 * The idea for this Check has been shamelessly stolen from the equivalent 047 * <a href="https://pmd.github.io/">PMD</a> rule. 048 * </p> 049 * <p> 050 * To configure the check: 051 * </p> 052 * <pre> 053 * <module name="SimplifyBooleanReturn"/> 054 * </pre> 055 * <p>Example:</p> 056 * <pre> 057 * public class Test { 058 * 059 * private boolean cond; 060 * private Foo a; 061 * private Foo b; 062 * 063 * public boolean check1() { 064 * if (cond) { // violation, can be simplified 065 * return true; 066 * } 067 * else { 068 * return false; 069 * } 070 * } 071 * 072 * // Ok, simplified version of check1() 073 * public boolean check2() { 074 * return cond; 075 * } 076 * 077 * // violations, can be simplified 078 * public boolean check3() { 079 * if (cond == true) { // can be simplified to "if (cond)" 080 * return false; 081 * } 082 * else { 083 * return true; // can be simplified to "return !cond" 084 * } 085 * } 086 * 087 * // Ok, can be simplified but doesn't return a Boolean 088 * public Foo choose1() { 089 * if (cond) { 090 * return a; 091 * } 092 * else { 093 * return b; 094 * } 095 * } 096 * 097 * // Ok, simplified version of choose1() 098 * public Foo choose2() { 099 * return cond ? a: b; 100 * } 101 * 102 * } 103 * </pre> 104 * <p> 105 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 106 * </p> 107 * <p> 108 * Violation Message Keys: 109 * </p> 110 * <ul> 111 * <li> 112 * {@code simplify.boolReturn} 113 * </li> 114 * </ul> 115 * 116 * @since 3.0 117 */ 118@StatelessCheck 119public class SimplifyBooleanReturnCheck 120 extends AbstractCheck { 121 122 /** 123 * A key is pointing to the warning message text in "messages.properties" 124 * file. 125 */ 126 public static final String MSG_KEY = "simplify.boolReturn"; 127 128 @Override 129 public int[] getAcceptableTokens() { 130 return getRequiredTokens(); 131 } 132 133 @Override 134 public int[] getDefaultTokens() { 135 return getRequiredTokens(); 136 } 137 138 @Override 139 public int[] getRequiredTokens() { 140 return new int[] {TokenTypes.LITERAL_IF}; 141 } 142 143 @Override 144 public void visitToken(DetailAST ast) { 145 // LITERAL_IF has the following four or five children: 146 // '(' 147 // condition 148 // ')' 149 // thenStatement 150 // [ LITERAL_ELSE (with the elseStatement as a child) ] 151 152 // don't bother if this is not if then else 153 final DetailAST elseLiteral = 154 ast.findFirstToken(TokenTypes.LITERAL_ELSE); 155 if (elseLiteral != null) { 156 final DetailAST elseStatement = elseLiteral.getFirstChild(); 157 158 // skip '(' and ')' 159 final DetailAST condition = ast.getFirstChild().getNextSibling(); 160 final DetailAST thenStatement = condition.getNextSibling().getNextSibling(); 161 162 if (canReturnOnlyBooleanLiteral(thenStatement) 163 && canReturnOnlyBooleanLiteral(elseStatement)) { 164 log(ast, MSG_KEY); 165 } 166 } 167 } 168 169 /** 170 * Returns if an AST is a return statement with a boolean literal 171 * or a compound statement that contains only such a return statement. 172 * 173 * <p>Returns {@code true} iff ast represents 174 * <pre> 175 * return true/false; 176 * </pre> 177 * or 178 * <pre> 179 * { 180 * return true/false; 181 * } 182 * </pre> 183 * 184 * @param ast the syntax tree to check 185 * @return if ast is a return statement with a boolean literal. 186 */ 187 private static boolean canReturnOnlyBooleanLiteral(DetailAST ast) { 188 boolean result = true; 189 if (!isBooleanLiteralReturnStatement(ast)) { 190 final DetailAST firstStatement = ast.getFirstChild(); 191 result = isBooleanLiteralReturnStatement(firstStatement); 192 } 193 return result; 194 } 195 196 /** 197 * Returns if an AST is a return statement with a boolean literal. 198 * 199 * <p>Returns {@code true} iff ast represents 200 * <pre> 201 * return true/false; 202 * </pre> 203 * 204 * @param ast the syntax tree to check 205 * @return if ast is a return statement with a boolean literal. 206 */ 207 private static boolean isBooleanLiteralReturnStatement(DetailAST ast) { 208 boolean booleanReturnStatement = false; 209 210 if (ast != null && ast.getType() == TokenTypes.LITERAL_RETURN) { 211 final DetailAST expr = ast.getFirstChild(); 212 213 if (expr.getType() != TokenTypes.SEMI) { 214 final DetailAST value = expr.getFirstChild(); 215 booleanReturnStatement = TokenUtil.isBooleanLiteralType(value.getType()); 216 } 217 } 218 return booleanReturnStatement; 219 } 220}