001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2026 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 * <div>
030 * Checks for over-complicated boolean return or yield statements.
031 * For example the following code
032 * </div>
033 * {@snippet lang="text" :
034 * if (valid())
035 *   return false;
036 * else
037 *   return true;
038 * }
039 *
040 * <p>
041 * could be written as
042 * </p>
043 * {@snippet lang="text" :
044 * return !valid();
045 * }
046 *
047 * <p>
048 * The idea for this Check has been shamelessly stolen from the equivalent
049 * <a href="https://pmd.github.io/pmd/pmd_rules_java_design.html#simplifybooleanreturns">
050 *     PMD</a> rule.
051 * </p>
052 *
053 * @since 3.0
054 */
055@StatelessCheck
056public class SimplifyBooleanReturnCheck extends AbstractCheck {
057
058    /**
059     * A key is pointing to the warning message text in "messages.properties" file.
060     */
061    public static final String MSG_KEY = "simplify.boolReturn";
062
063    /**
064     * Creates a new {@code SimplifyBooleanReturnCheck} instance.
065     */
066    public SimplifyBooleanReturnCheck() {
067        // no code by default
068    }
069
070    @Override
071    public int[] getAcceptableTokens() {
072        return getRequiredTokens();
073    }
074
075    @Override
076    public int[] getDefaultTokens() {
077        return getRequiredTokens();
078    }
079
080    @Override
081    public int[] getRequiredTokens() {
082        return new int[] {TokenTypes.LITERAL_IF};
083    }
084
085    @Override
086    public void visitToken(DetailAST ast) {
087        // LITERAL_IF has the following four or five children:
088        // '('
089        // condition
090        // ')'
091        // thenStatement
092        // [ LITERAL_ELSE (with the elseStatement as a child) ]
093
094        // don't bother if this is not if then else
095        final DetailAST elseLiteral =
096            ast.findFirstToken(TokenTypes.LITERAL_ELSE);
097        if (elseLiteral != null) {
098            final DetailAST elseStatement = elseLiteral.getFirstChild();
099
100            // skip '(' and ')'
101            final DetailAST condition = ast.getFirstChild().getNextSibling();
102            final DetailAST thenStatement = condition.getNextSibling().getNextSibling();
103
104            if (canReturnOrYieldOnlyBooleanLiteral(thenStatement)
105                && canReturnOrYieldOnlyBooleanLiteral(elseStatement)) {
106                log(ast, MSG_KEY);
107            }
108        }
109    }
110
111    /**
112     * Returns if an AST is a return or a yield statement with a boolean literal
113     * or a compound statement that contains only such a return or a yield statement.
114     *
115     * <p>Returns {@code true} iff ast represents
116     * {@snippet lang="text" :
117     * return/yield true/false;
118     * }
119     * or
120     * {@snippet lang="text" :
121     * {
122     *   return/yield true/false;
123     * }
124     * }
125     *
126     * @param ast the syntax tree to check
127     * @return if ast is a return or a yield statement with a boolean literal.
128     */
129    private static boolean canReturnOrYieldOnlyBooleanLiteral(DetailAST ast) {
130        boolean result = true;
131        if (!isBooleanLiteralReturnOrYieldStatement(ast)) {
132            final DetailAST firstStatement = ast.getFirstChild();
133            result = isBooleanLiteralReturnOrYieldStatement(firstStatement);
134        }
135        return result;
136    }
137
138    /**
139     * Returns if an AST is a return or a yield statement with a boolean literal.
140     *
141     * <p>Returns {@code true} iff ast represents
142     * {@snippet lang="text" :
143     * return/yield true/false;
144     * }
145     *
146     * @param ast the syntax tree to check
147     * @return if ast is a return or a yield statement with a boolean literal.
148     */
149    private static boolean isBooleanLiteralReturnOrYieldStatement(DetailAST ast) {
150        boolean booleanReturnStatement = false;
151
152        if (ast != null && (ast.getType() == TokenTypes.LITERAL_RETURN
153                                || ast.getType() == TokenTypes.LITERAL_YIELD)) {
154            final DetailAST expr = ast.getFirstChild();
155
156            if (expr.getType() != TokenTypes.SEMI) {
157                final DetailAST value = expr.getFirstChild();
158                booleanReturnStatement = TokenUtil.isBooleanLiteralType(value.getType());
159            }
160        }
161        return booleanReturnStatement;
162    }
163
164}