View Javadoc

1   package net.sourceforge.pmd.lang.java.rule.design;
2   
3   import net.sourceforge.pmd.lang.ast.Node;
4   import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
5   import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
6   import net.sourceforge.pmd.lang.java.ast.ASTInitializer;
7   import net.sourceforge.pmd.lang.java.ast.ASTName;
8   import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
9   import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
10  
11  public class CompareObjectsWithEqualsRule extends AbstractJavaRule {
12  
13      private boolean hasName(Node n) {
14          return n.jjtGetNumChildren() > 0 && n.jjtGetChild(0) instanceof ASTName;
15      }
16      
17      /**
18  	 * Indicate whether this node is allocating a new object.
19  	 * 
20  	 * @param n
21  	 *            node that might be allocating a new object
22  	 * @return true if child 0 is an AllocationExpression
23  	 */
24  	private boolean isAllocation(Node n) {
25          return n.jjtGetNumChildren() > 0 && n.jjtGetChild(0) instanceof ASTAllocationExpression && n.jjtGetParent().jjtGetNumChildren() == 1;
26  	}
27          
28      public Object visit(ASTEqualityExpression node, Object data) {
29          Node c0 = node.jjtGetChild(0).jjtGetChild(0);
30          Node c1 = node.jjtGetChild(1).jjtGetChild(0);
31  
32          // If either side is allocating a new object, there's no way an
33          // equals expression is correct
34          if ((isAllocation(c0)) || (isAllocation(c1))) {
35              addViolation(data, node);
36              return data;
37          }
38          
39          // skip if either child is not a simple name
40          if (!hasName(c0) || !hasName(c1)) {
41              return data;
42          }
43  
44          // skip if either is a qualified name
45          if (isQualifiedName(c0.jjtGetChild(0)) || isQualifiedName(c1.jjtGetChild(0))) {
46              return data;
47          }
48  
49          // skip static initializers... missing some cases here
50          if (!node.getParentsOfType(ASTInitializer.class).isEmpty()) {
51              return data;
52          }
53                
54          ASTName n0 = (ASTName) c0.jjtGetChild(0);
55          ASTName n1 = (ASTName) c1.jjtGetChild(0);
56  
57          if (n0.getNameDeclaration() instanceof VariableNameDeclaration && n1.getNameDeclaration() instanceof VariableNameDeclaration) {
58              VariableNameDeclaration nd0 = (VariableNameDeclaration) n0.getNameDeclaration();
59              VariableNameDeclaration nd1 = (VariableNameDeclaration) n1.getNameDeclaration();
60  
61              // skip array dereferences... this misses some cases
62              // FIXME catch comparisons btwn array elements of reference types
63              if (nd0.isArray() || nd1.isArray()) {
64                  return data;
65              }
66  
67              if (nd0.isReferenceType() && nd1.isReferenceType()) {
68                  addViolation(data, node);
69              }
70          }
71  
72          return data;
73      }
74  }