001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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 antlr.collections.AST; 023import com.puppycrawl.tools.checkstyle.api.Check; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025import com.puppycrawl.tools.checkstyle.api.DetailAST; 026 027 028/** 029 * <p> 030 * Checks for overly complicated boolean return statements. 031 * Idea shamelessly stolen from the equivalent PMD rule (pmd.sourceforge.net). 032 * </p> 033 * <p> 034 * An example of how to configure the check is: 035 * </p> 036 * <pre> 037 * <module name="SimplifyBooleanReturn"/> 038 * </pre> 039 * @author Lars Kühne 040 */ 041public class SimplifyBooleanReturnCheck 042 extends Check 043{ 044 @Override 045 public int[] getDefaultTokens() 046 { 047 return new int[] {TokenTypes.LITERAL_IF}; 048 } 049 050 @Override 051 public void visitToken(DetailAST ast) 052 { 053 // LITERAL_IF has the following four or five children: 054 // '(' 055 // condition 056 // ')' 057 // thenStatement 058 // [ LITERAL_ELSE (with the elseStatement as a child) ] 059 060 // don't bother if this is not if then else 061 final AST elseLiteral = 062 ast.findFirstToken(TokenTypes.LITERAL_ELSE); 063 if (elseLiteral == null) { 064 return; 065 } 066 final AST elseStatement = elseLiteral.getFirstChild(); 067 068 // skip '(' and ')' 069 // TODO: Introduce helpers in DetailAST 070 final AST condition = ast.getFirstChild().getNextSibling(); 071 final AST thenStatement = condition.getNextSibling().getNextSibling(); 072 073 if (returnsOnlyBooleanLiteral(thenStatement) 074 && returnsOnlyBooleanLiteral(elseStatement)) 075 { 076 log(ast.getLineNo(), ast.getColumnNo(), "simplify.boolreturn"); 077 } 078 } 079 080 /** 081 * Returns if an AST is a return statment with a boolean literal 082 * or a compound statement that contains only such a return statement. 083 * 084 * Returns <code>true</code> iff ast represents 085 * <br/> 086 * <pre> 087 * return true/false; 088 * </pre> 089 * or 090 * <br/> 091 * <pre> 092 * { 093 * return true/false; 094 * } 095 * </pre> 096 * 097 * @param ast the sytax tree to check 098 * @return if ast is a return statment with a boolean literal. 099 */ 100 private static boolean returnsOnlyBooleanLiteral(AST ast) 101 { 102 if (isBooleanLiteralReturnStatement(ast)) { 103 return true; 104 } 105 106 final AST firstStmnt = ast.getFirstChild(); 107 return isBooleanLiteralReturnStatement(firstStmnt); 108 } 109 110 /** 111 * Returns if an AST is a return statment with a boolean literal. 112 * 113 * Returns <code>true</code> iff ast represents 114 * <br/> 115 * <pre> 116 * return true/false; 117 * </pre> 118 * 119 * @param ast the sytax tree to check 120 * @return if ast is a return statment with a boolean literal. 121 */ 122 private static boolean isBooleanLiteralReturnStatement(AST ast) 123 { 124 if ((ast == null) || (ast.getType() != TokenTypes.LITERAL_RETURN)) { 125 return false; 126 } 127 128 final AST expr = ast.getFirstChild(); 129 130 if ((expr == null) || (expr.getType() == TokenTypes.SEMI)) { 131 return false; 132 } 133 134 final AST value = expr.getFirstChild(); 135 return isBooleanLiteralType(value.getType()); 136 } 137 138 /** 139 * Checks if a token type is a literal true or false. 140 * @param tokenType the TokenType 141 * @return true iff tokenType is LITERAL_TRUE or LITERAL_FALSE 142 */ 143 private static boolean isBooleanLiteralType(final int tokenType) 144 { 145 final boolean iastrue = (tokenType == TokenTypes.LITERAL_TRUE); 146 final boolean isFalse = (tokenType == TokenTypes.LITERAL_FALSE); 147 return iastrue || isFalse; 148 } 149}