View Javadoc

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