private static ImmutableMap<String, AnnotatedTypeMirror> rewriteParameterTypes(
      Executable executable,
      TypeElement builderType,
      ErrorReporter errorReporter,
      Types typeUtils) {
    ImmutableList<TypeParameterElement> executableTypeParams = executable.typeParameters();
    List<? extends TypeParameterElement> builderTypeParams = builderType.getTypeParameters();
    if (!BuilderSpec.sameTypeParameters(executableTypeParams, builderTypeParams)) {
      errorReporter.abortWithError(
          builderType,
          "[AutoBuilderTypeParams] Builder type parameters %s must match type parameters %s of %s",
          TypeEncoder.typeParametersString(builderTypeParams),
          TypeEncoder.typeParametersString(executableTypeParams),
          executable);
    }
    if (executableTypeParams.isEmpty()) {
      // Optimization for a common case. No point in doing all that type visiting if we have no
      // variables to substitute.
      return executable.parameters().stream()
          .collect(
              toImmutableMap(
                  v -> v.getSimpleName().toString(), v -> new AnnotatedTypeMirror(v.asType())));
    }
    Map<Equivalence.Wrapper<TypeVariable>, TypeMirror> typeVariables = new LinkedHashMap<>();
    for (int i = 0; i < executableTypeParams.size(); i++) {
      TypeVariable from = MoreTypes.asTypeVariable(executableTypeParams.get(i).asType());
      TypeVariable to = MoreTypes.asTypeVariable(builderTypeParams.get(i).asType());
      typeVariables.put(MoreTypes.equivalence().wrap(from), to);
    }
    Function<TypeVariable, TypeMirror> substitute =
        v -> typeVariables.get(MoreTypes.equivalence().wrap(v));
    return executable.parameters().stream()
        .collect(
            toImmutableMap(
                v -> v.getSimpleName().toString(),
                v ->
                    new AnnotatedTypeMirror(
                        v.asType(),
                        TypeVariables.substituteTypeVariables(v.asType(), substitute, typeUtils))));
  }
Classifies the given methods from a builder type and its ancestors.

@param methods the abstract methods in {@code builderType} and its ancestors.
@param errorReporter where to report errors.
@param processingEnv the ProcessingEnvironment for annotation processing.
@param executable the constructor or static method that AutoBuilder will call.
@param builtType the type to be built.
@param builderType the builder class or interface within {@code ofClass}.
@param propertiesWithDefaults properties that have a default value, so it is not an error for
    them not to have a setter.
@return an {@code Optional} that contains the results of the classification if it was
    successful or nothing if it was not.
value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java