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