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