View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.java.rule.design;
5   
6   import net.sourceforge.pmd.lang.ast.Node;
7   import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
8   import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
9   import net.sourceforge.pmd.lang.java.ast.ASTInitializer;
10  import net.sourceforge.pmd.lang.java.ast.ASTName;
11  import net.sourceforge.pmd.lang.java.ast.ASTReferenceType;
12  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
13  import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
14  
15  public class CompareObjectsWithEqualsRule extends AbstractJavaRule {
16  
17      private boolean hasName(Node n) {
18          return n.jjtGetNumChildren() > 0 && n.jjtGetChild(0) instanceof ASTName;
19      }
20      
21      /**
22  	 * Indicate whether this node is allocating a new object.
23  	 * 
24  	 * @param n
25  	 *            node that might be allocating a new object
26  	 * @return true if child 0 is an AllocationExpression
27  	 */
28  	private boolean isAllocation(Node n) {
29          return n.jjtGetNumChildren() > 0 && n.jjtGetChild(0) instanceof ASTAllocationExpression && 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(c0.jjtGetChild(0)) || isQualifiedName(c1.jjtGetChild(0))) {
50              return data;
51          }
52  
53          // skip static initializers... missing some cases here
54          if (!node.getParentsOfType(ASTInitializer.class).isEmpty()) {
55              return data;
56          }
57                
58          ASTName n0 = (ASTName) c0.jjtGetChild(0);
59          ASTName n1 = (ASTName) c1.jjtGetChild(0);
60  
61          if (n0.getNameDeclaration() instanceof VariableNameDeclaration && n1.getNameDeclaration() instanceof VariableNameDeclaration) {
62              VariableNameDeclaration nd0 = (VariableNameDeclaration) n0.getNameDeclaration();
63              VariableNameDeclaration nd1 = (VariableNameDeclaration) n1.getNameDeclaration();
64  
65              // skip array dereferences... this misses some cases
66              // FIXME catch comparisons btwn array elements of reference types
67              if (nd0.isArray() || nd1.isArray()) {
68                  return data;
69              }
70  
71              if (nd0.isReferenceType() && nd1.isReferenceType()) {
72  
73                  ASTReferenceType type0 = (ASTReferenceType)((Node) nd0.getAccessNodeParent()).jjtGetChild(0).jjtGetChild(0);
74                  ASTReferenceType type1 = (ASTReferenceType)((Node) nd1.getAccessNodeParent()).jjtGetChild(0).jjtGetChild(0);
75                  // skip, if it is an enum
76                  if (type0.getType() != null && type0.getType().equals(type1.getType()) && type0.getType().isEnum()) {
77                      return data;
78                  }
79  
80                  addViolation(data, node);
81              }
82          }
83  
84          return data;
85      }
86  }