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 }