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