docs: public static boolean sameVariable(ExpressionTree expr1, ExpressionTree expr2) {
      requireNonNull(expr1);
      requireNonNull(expr2);
      // Throw up our hands if we're not comparing identifiers and/or field accesses.
      if ((expr1.getKind() != Kind.IDENTIFIER && expr1.getKind() != Kind.MEMBER_SELECT)
          || (expr2.getKind() != Kind.IDENTIFIER && expr2.getKind() != Kind.MEMBER_SELECT)) {
        return false;
      }
  
      Symbol sym1 = getSymbol(expr1);
      Symbol sym2 = getSymbol(expr2);
      if (sym1 == null) {
        throw new IllegalStateException("Couldn't get symbol for " + expr1);
      } else if (sym2 == null) {
        throw new IllegalStateException("Couldn't get symbol for " + expr2);
      }
  
      if (expr1.getKind() == Kind.IDENTIFIER && expr2.getKind() == Kind.IDENTIFIER) {
        // foo == foo?
        return sym1.equals(sym2);
      } else if (expr1.getKind() == Kind.MEMBER_SELECT && expr2.getKind() == Kind.MEMBER_SELECT) {
        // foo.baz.bar == foo.baz.bar?
        return sym1.equals(sym2)
            && sameVariable(((JCFieldAccess) expr1).selected, ((JCFieldAccess) expr2).selected);
      } else {
        // this.foo == foo?
        ExpressionTree selected = null;
        if (expr1.getKind() == Kind.IDENTIFIER) {
          selected = ((JCFieldAccess) expr2).selected;
        } else {
          selected = ((JCFieldAccess) expr1).selected;
        }
        // TODO(eaftan): really shouldn't be relying on .toString()
        return selected.toString().equals("this") && sym1.equals(sym2);
      }
    }