View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.rules;
    5
    6   import net.sourceforge.pmd.AbstractRule;
    7   import net.sourceforge.pmd.ast.ASTBlock;
    8   import net.sourceforge.pmd.ast.ASTBlockStatement;
    9   import net.sourceforge.pmd.ast.ASTBooleanLiteral;
    10  import net.sourceforge.pmd.ast.ASTIfStatement;
    11  import net.sourceforge.pmd.ast.ASTLiteral;
    12  import net.sourceforge.pmd.ast.ASTMethodDeclaration;
    13  import net.sourceforge.pmd.ast.ASTPrimaryExpression;
    14  import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
    15  import net.sourceforge.pmd.ast.ASTPrimitiveType;
    16  import net.sourceforge.pmd.ast.ASTResultType;
    17  import net.sourceforge.pmd.ast.ASTReturnStatement;
    18  import net.sourceforge.pmd.ast.ASTStatement;
    19  import net.sourceforge.pmd.ast.ASTUnaryExpressionNotPlusMinus;
    20  import net.sourceforge.pmd.ast.Node;
    21  import net.sourceforge.pmd.ast.SimpleNode;
    22
    23  public class SimplifyBooleanReturns extends AbstractRule {
    24
    25      public Object visit(ASTMethodDeclaration node, Object data) {
    26          // only boolean methods should be inspected
    27          ASTResultType r = node.getResultType();
    28
    29          if (!r.isVoid()) {
    30              Node t = r.jjtGetChild(0);
    31              if (t.jjtGetNumChildren() == 1) {
    32                  t = t.jjtGetChild(0);
    33                  if ((t instanceof ASTPrimitiveType) && ((ASTPrimitiveType) t).isBoolean()) {
    34                      return super.visit(node, data);
    35                  }
    36              }
    37          }
    38          // skip method
    39          return data;
    40      }
    41
    42      public Object visit(ASTIfStatement node, Object data) {
    43          // only deal with if..then..else stmts
    44          if (node.jjtGetNumChildren() != 3) {
    45              return super.visit(node, data);
    46          }
    47
    48          // don't bother if either the if or the else block is empty
    49          if (node.jjtGetChild(1).jjtGetNumChildren() == 0 || node.jjtGetChild(2).jjtGetNumChildren() == 0) {
    50              return super.visit(node, data);
    51          }
    52
    53          // if we have something like
    54          // if(true) or if(false)
    55          if (false && // FIXME: disabling moved in first position to avoid NPE but why is this here?
    56              node.jjtGetChild(0).jjtGetChild(0) instanceof ASTPrimaryExpression &&
    57              node.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0) instanceof ASTPrimaryPrefix &&
    58              node.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral &&
    59              node.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0).jjtGetChild(0).jjtGetChild(0) instanceof ASTBooleanLiteral) {
    60            addViolation(data, node);
    61          }
    62          else {
    63            SimpleNode returnStatement1 = (SimpleNode)node.jjtGetChild(1).jjtGetChild(0);
    64            SimpleNode returnStatement2 = (SimpleNode)node.jjtGetChild(2).jjtGetChild(0);
    65
    66            if (returnStatement1 instanceof ASTReturnStatement && returnStatement2 instanceof ASTReturnStatement) {
    67              //if we have 2 return;
    68              if(isSimpleReturn(returnStatement1) && isSimpleReturn(returnStatement2)) {
    69                  // first case:
    70                  // If
    71                  //  Expr
    72                  //  Statement
    73                  //   ReturnStatement
    74                  //  Statement
    75                  //   ReturnStatement
    76                  // i.e.,
    77                  // if (foo)
    78                  //  return true;
    79                  // else
    80                  //  return false;
    81                addViolation(data, node);
    82              }
    83              else {
    84                SimpleNode expression1 = (SimpleNode)returnStatement1.jjtGetChild(0).jjtGetChild(0);
    85                SimpleNode expression2 = (SimpleNode)returnStatement2.jjtGetChild(0).jjtGetChild(0);
    86                if(terminatesInBooleanLiteral(returnStatement1) && terminatesInBooleanLiteral(returnStatement2)) {
    87                  addViolation(data, node);
    88                }
    89                else if (expression1 instanceof ASTUnaryExpressionNotPlusMinus ^ expression2 instanceof ASTUnaryExpressionNotPlusMinus) {
    90                  //We get the nodes under the '!' operator
    91                  //If they are the same => error
    92                  if(isNodesEqualWithUnaryExpression(expression1, expression2)) {
    93                      // second case:
    94                      // If
    95                      //  Expr
    96                      //  Statement
    97                      //   ReturnStatement
    98                      //     UnaryExpressionNotPlusMinus '!'
    99                      //       Expression E
    100                     //  Statement
    101                     //   ReturnStatement
    102                     //       Expression E
    103                     // i.e.,
    104                     // if (foo)
    105                     //  return !a;
    106                     // else
    107                     //  return a;
    108                   addViolation(data, node);
    109                 }
    110               }
    111             }
    112           } else if (hasOneBlockStmt((SimpleNode) node.jjtGetChild(1)) && hasOneBlockStmt((SimpleNode) node.jjtGetChild(2))) {
    113             //We have blocks so we must go down three levels (BlockStatement, Statement, ReturnStatement)
    114             returnStatement1 = (SimpleNode)returnStatement1.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0);
    115             returnStatement2 = (SimpleNode)returnStatement2.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0);
    116
    117             //if we have 2 return;
    118             if(isSimpleReturn(returnStatement1) && isSimpleReturn(returnStatement2)) {
    119                 // third case
    120                 // If
    121                 // Expr
    122                 // Statement
    123                 //  Block
    124                 //   BlockStatement
    125                 //    Statement
    126                 //     ReturnStatement
    127                 // Statement
    128                 //  Block
    129                 //   BlockStatement
    130                 //    Statement
    131                 //     ReturnStatement
    132                 // i.e.,
    133                 // if (foo) {
    134                 //  return true;
    135                 // } else {
    136                 //  return false;
    137                 // }
    138               addViolation(data, node);
    139             }
    140             else {
    141               SimpleNode expression1 = getDescendant(returnStatement1, 4);
    142               SimpleNode expression2 = getDescendant(returnStatement2, 4);
    143               if(terminatesInBooleanLiteral((SimpleNode)node.jjtGetChild(1).jjtGetChild(0)) && terminatesInBooleanLiteral((SimpleNode) node.jjtGetChild(2).jjtGetChild(0))) {
    144                 addViolation(data, node);
    145               } else if (expression1 instanceof ASTUnaryExpressionNotPlusMinus ^ expression2 instanceof ASTUnaryExpressionNotPlusMinus) {
    146                 //We get the nodes under the '!' operator
    147                 //If they are the same => error
    148                 if(isNodesEqualWithUnaryExpression(expression1, expression2)) {
    149                     // forth case
    150                     // If
    151                     // Expr
    152                     // Statement
    153                     //  Block
    154                     //   BlockStatement
    155                     //    Statement
    156                     //     ReturnStatement
    157                     //       UnaryExpressionNotPlusMinus '!'
    158                     //         Expression E
    159                     // Statement
    160                     //  Block
    161                     //   BlockStatement
    162                     //    Statement
    163                     //     ReturnStatement
    164                     //      Expression E
    165                     // i.e.,
    166                     // if (foo) {
    167                     //  return !a;
    168                     // } else {
    169                     //  return a;
    170                     // }
    171                   addViolation(data, node);
    172                 }
    173               }
    174             }
    175           }
    176         }
    177         return super.visit(node, data);
    178     }
    179
    180     private boolean hasOneBlockStmt(SimpleNode node) {
    181         return node.jjtGetChild(0) instanceof ASTBlock && node.jjtGetChild(0).jjtGetNumChildren() == 1 && node.jjtGetChild(0).jjtGetChild(0) instanceof ASTBlockStatement && node.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0) instanceof ASTStatement && node.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0).jjtGetChild(0) instanceof ASTReturnStatement;
    182     }
    183
    184     /**
    185      * Returns the first child node going down 'level' levels or null if level is invalid
    186      */
    187     private SimpleNode getDescendant(SimpleNode node, int level) {
    188         Node n = node;
    189         for(int i = 0; i < level; i++) {
    190             if (n.jjtGetNumChildren() == 0) {
    191                 return null;
    192             }
    193             n = n.jjtGetChild(0);
    194         }
    195         return (SimpleNode) n;
    196     }
    197
    198     private boolean terminatesInBooleanLiteral(SimpleNode node) {
    199
    200         return eachNodeHasOneChild(node) && (getLastChild(node) instanceof ASTBooleanLiteral);
    201     }
    202
    203     private boolean eachNodeHasOneChild(SimpleNode node) {
    204         if (node.jjtGetNumChildren() > 1) {
    205             return false;
    206         }
    207         if (node.jjtGetNumChildren() == 0) {
    208             return true;
    209         }
    210         return eachNodeHasOneChild((SimpleNode) node.jjtGetChild(0));
    211     }
    212
    213     private SimpleNode getLastChild(SimpleNode node) {
    214         if (node.jjtGetNumChildren() == 0) {
    215             return node;
    216         }
    217         return getLastChild((SimpleNode) node.jjtGetChild(0));
    218     }
    219
    220     private boolean isNodesEqualWithUnaryExpression(SimpleNode n1, SimpleNode n2) {
    221       SimpleNode node1, node2;
    222       if(n1 instanceof ASTUnaryExpressionNotPlusMinus) {
    223         node1 = (SimpleNode)n1.jjtGetChild(0);
    224       } else {
    225         node1 = n1;
    226       }
    227       if(n2 instanceof ASTUnaryExpressionNotPlusMinus) {
    228         node2 = (SimpleNode)n2.jjtGetChild(0);
    229       } else {
    230         node2 = n2;
    231       }
    232       return isNodesEquals(node1, node2);
    233     }
    234
    235     private boolean isNodesEquals(SimpleNode n1, SimpleNode n2) {
    236         int numberChild1 = n1.jjtGetNumChildren();
    237         int numberChild2 = n2.jjtGetNumChildren();
    238         if(numberChild1 != numberChild2)
    239           return false;
    240         if(!n1.getClass().equals(n2.getClass()))
    241           return false;
    242         if(!n1.toString().equals(n2.toString()))
    243           return false;
    244         for(int i = 0 ; i < numberChild1 ; i++) {
    245           if( !isNodesEquals( (SimpleNode)n1.jjtGetChild(i), (SimpleNode)n2.jjtGetChild(i) ) )
    246             return false;
    247         }
    248         return true;
    249     }
    250
    251     private boolean isSimpleReturn(SimpleNode node) {
    252       return ( ( node instanceof ASTReturnStatement ) && ( node.jjtGetNumChildren() == 0 ) );
    253     }
    254
    255 }