CompareObjectsWithEqualsRule.java
01 package net.sourceforge.pmd.lang.java.rule.design;
02 
03 import net.sourceforge.pmd.lang.ast.Node;
04 import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
05 import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
06 import net.sourceforge.pmd.lang.java.ast.ASTInitializer;
07 import net.sourceforge.pmd.lang.java.ast.ASTName;
08 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
09 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() && n.jjtGetChild(0instanceof 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() && n.jjtGetChild(0instanceof 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 = (ASTNamec0.jjtGetChild(0);
55         ASTName n1 = (ASTNamec1.jjtGetChild(0);
56 
57         if (n0.getNameDeclaration() instanceof VariableNameDeclaration && n1.getNameDeclaration() instanceof VariableNameDeclaration) {
58             VariableNameDeclaration nd0 = (VariableNameDeclarationn0.getNameDeclaration();
59             VariableNameDeclaration nd1 = (VariableNameDeclarationn1.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 }