@Override
    public Boolean visitDeclared(DeclaredType a, EqualVisitorParam p) {
      if (p.type.getKind().equals(DECLARED)) {
        DeclaredType b = (DeclaredType) p.type;
        Element aElement = a.asElement();
        Element bElement = b.asElement();
        Set<ComparedElements> newVisiting =
            visitingSetPlus(
                p.visiting, aElement, a.getTypeArguments(), bElement, b.getTypeArguments());
        if (newVisiting.equals(p.visiting)) {
          // We're already visiting this pair of elements.
          // This can happen for example with Enum in Enum<E extends Enum<E>>. Return a
          // provisional true value since if the Elements are not in fact equal the original
          // visitor of Enum will discover that. We have to check both Elements being compared
          // though to avoid missing the fact that one of the types being compared
          // differs at exactly this point.
          return true;
        }
        return aElement.equals(bElement)
            && equal(enclosingType(a), enclosingType(b), newVisiting)
            && equalLists(a.getTypeArguments(), b.getTypeArguments(), newVisiting);
      }
      return false;
    }
Returns an {@link Equivalence} that can be used to compare types. The standard way to compare
types is {@link javax.lang.model.util.Types#isSameType Types.isSameType}, but this alternative
may be preferred in a number of cases:

<ul>
  <li>If you don't have an instance of {@code Types}.
  <li>If you want a reliable {@code hashCode()} for the types, for example to construct a set
      of types using {@link java.util.HashSet} with {@link Equivalence#wrap(Object)}.
  <li>If you want distinct type variables to be considered equal if they have the same names
      and bounds.
  <li>If you want wildcard types to compare equal if they have the same bounds. {@code
      Types.isSameType} never considers wildcards equal, even when comparing a type to itself.
</ul>
common/src/main/java/com/google/auto/common/MoreTypes.java