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() > 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 }
|