blob: 00c38d0ea93d70c4db0c490f5f1690e235fb3d96 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2017 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.checks.coding;
import antlr.collections.AST;
import com.puppycrawl.tools.checkstyle.StatelessCheck;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
/**
* <p>
* Checks for overly complicated boolean return statements.
* Idea shamelessly stolen from the equivalent PMD rule (pmd.sourceforge.net).
* </p>
* <p>
* An example of how to configure the check is:
* </p>
* <pre>
* &lt;module name="SimplifyBooleanReturn"/&gt;
* </pre>
* @author Lars Kühne
*/
@StatelessCheck
public class SimplifyBooleanReturnCheck
extends AbstractCheck {
/**
* A key is pointing to the warning message text in "messages.properties"
* file.
*/
public static final String MSG_KEY = "simplify.boolReturn";
@Override
public int[] getAcceptableTokens() {
return new int[] {TokenTypes.LITERAL_IF};
}
@Override
public int[] getDefaultTokens() {
return getAcceptableTokens();
}
@Override
public int[] getRequiredTokens() {
return getAcceptableTokens();
}
@Override
public void visitToken(DetailAST ast) {
// LITERAL_IF has the following four or five children:
// '('
// condition
// ')'
// thenStatement
// [ LITERAL_ELSE (with the elseStatement as a child) ]
// don't bother if this is not if then else
final AST elseLiteral =
ast.findFirstToken(TokenTypes.LITERAL_ELSE);
if (elseLiteral != null) {
final AST elseStatement = elseLiteral.getFirstChild();
// skip '(' and ')'
final AST condition = ast.getFirstChild().getNextSibling();
final AST thenStatement = condition.getNextSibling().getNextSibling();
if (canReturnOnlyBooleanLiteral(thenStatement)
&& canReturnOnlyBooleanLiteral(elseStatement)) {
log(ast.getLineNo(), ast.getColumnNo(), MSG_KEY);
}
}
}
/**
* Returns if an AST is a return statement with a boolean literal
* or a compound statement that contains only such a return statement.
*
* <p>Returns {@code true} iff ast represents
* <br/>
* <pre>
* return true/false;
* </pre>
* or
* <br/>
* <pre>
* {
* return true/false;
* }
* </pre>
*
* @param ast the syntax tree to check
* @return if ast is a return statement with a boolean literal.
*/
private static boolean canReturnOnlyBooleanLiteral(AST ast) {
boolean result = true;
if (!isBooleanLiteralReturnStatement(ast)) {
final AST firstStatement = ast.getFirstChild();
result = isBooleanLiteralReturnStatement(firstStatement);
}
return result;
}
/**
* Returns if an AST is a return statement with a boolean literal.
*
* <p>Returns {@code true} iff ast represents
* <br/>
* <pre>
* return true/false;
* </pre>
*
* @param ast the syntax tree to check
* @return if ast is a return statement with a boolean literal.
*/
private static boolean isBooleanLiteralReturnStatement(AST ast) {
boolean booleanReturnStatement = false;
if (ast != null && ast.getType() == TokenTypes.LITERAL_RETURN) {
final AST expr = ast.getFirstChild();
if (expr.getType() != TokenTypes.SEMI) {
final AST value = expr.getFirstChild();
booleanReturnStatement = isBooleanLiteralType(value.getType());
}
}
return booleanReturnStatement;
}
/**
* Checks if a token type is a literal true or false.
* @param tokenType the TokenType
* @return true iff tokenType is LITERAL_TRUE or LITERAL_FALSE
*/
private static boolean isBooleanLiteralType(final int tokenType) {
final boolean isTrue = tokenType == TokenTypes.LITERAL_TRUE;
final boolean isFalse = tokenType == TokenTypes.LITERAL_FALSE;
return isTrue || isFalse;
}
}