static ImmutableMap<ExecutableElement, AnnotatedTypeMirror> rewriteReturnTypes(
      Types typeUtils,
      Collection<ExecutableElement> methods,
      TypeElement sourceType,
      TypeElement targetType) {
    List<? extends TypeParameterElement> sourceTypeParameters = sourceType.getTypeParameters();
    List<? extends TypeParameterElement> targetTypeParameters = targetType.getTypeParameters();
    Preconditions.checkArgument(
        sourceTypeParameters.toString().equals(targetTypeParameters.toString()),
        "%s != %s",
        sourceTypeParameters,
        targetTypeParameters);
    // What we're doing is only valid if the type parameters are "the same". The check here even
    // requires the names to be the same. The logic would still work without that, but we impose
    // that requirement elsewhere and it means we can check in this simple way.
    TypeMirror[] targetTypeParameterMirrors = new TypeMirror[targetTypeParameters.size()];
    for (int i = 0; i < targetTypeParameters.size(); i++) {
      targetTypeParameterMirrors[i] = targetTypeParameters.get(i).asType();
    }
    DeclaredType parallelSource = typeUtils.getDeclaredType(sourceType, targetTypeParameterMirrors);
    return methods.stream()
        .collect(
            toImmutableMap(
                m -> m,
                m -> MethodSignature.asMemberOf(typeUtils, parallelSource, m).returnType()));
  }
Returns a map from methods to return types, where the return types are not necessarily the
original return types of the methods. Consider this example:

<pre>{@code
@AutoValue class Foo<T> {
  abstract T getFoo();

  @AutoValue.Builder
  abstract class Builder<T> {
    abstract Builder setFoo(T t);
    abstract Foo<T> build();
  }
}
}</pre>

We want to be able to check that the parameter type of {@code setFoo} is the same as the return
type of {@code getFoo}. But in fact it isn't, because the {@code T} of {@code Foo<T>} is not
the same as the {@code T} of {@code Foo.Builder<T>}. So we create a parallel {@code Foo<T>}
where the {@code T} <i>is</i> the one from {@code Foo.Builder<T>}. That way the types do
correspond. This method then returns the return types of the given methods as they appear in
that parallel class, meaning the type given for {@code getFoo()} is the {@code T} of {@code
Foo.Builder<T>}.

<p>We do the rewrite this way around (applying the type parameter from {@code Foo.Builder} to
{@code Foo}) because if we hit one of the historical Eclipse bugs with {@link Types#asMemberOf}
then {@link EclipseHack#methodReturnType} can use fallback logic, which only works for methods
with no arguments.

@param methods the methods whose return types are to be rewritten.
@param sourceType the class containing those methods ({@code Foo} in the example).
@param targetType the class to translate the methods into ({@code Foo.Builder<T>}) in the
    example.
value/src/main/java/com/google/auto/value/processor/TypeVariables.java