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 }