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  }