private Optional<Copier> getSetterFunction(E propertyElement, ExecutableElement setter) {
    VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters());
    boolean nullableParameter =
        nullableAnnotationFor(parameterElement, parameterElement.asType()).isPresent();
    String property = propertyElements().inverse().get(propertyElement);
    TypeMirror targetType = rewrittenPropertyTypes.get(property).getType();
    TypeMirror parameterType =
        MethodSignature.asMemberOf(typeUtils, builderType, setter)
            .parameterTypes()
            .get(0)
            .getType();
    // Two types are assignable to each other if they are the same type, or if one is primitive and
    // the other is the corresponding boxed type. There might be other cases where this is true, but
    // we're likely to want to accept those too.
    if (typeUtils.isAssignable(parameterType, targetType)
        && typeUtils.isAssignable(targetType, parameterType)) {
      if (nullableParameter) {
        boolean nullableProperty =
            nullableAnnotationFor(propertyElement, originalPropertyType(propertyElement))
                .isPresent();
        if (!nullableProperty) {
          errorReporter.reportError(
              setter,
              "[%sNullNotNull] Parameter of setter method is @Nullable but %s is not",
              autoWhat(),
              propertyString(propertyElement));
          return Optional.empty();
        }
      }
      if (!parameterElement.asType().getKind().isPrimitive()
          && originalPropertyType(propertyElement).getKind().isPrimitive()) {
        errorReporter
            .reportWarning(
            setter,
            "[%sUnnecessaryBoxing] %s is primitive but parameter of setter method is not",
            autoWhat(),
            propertyString(propertyElement));
      }
      return Optional.of(Copier.IDENTITY);
    }

    // Parameter type is not equal to property type, but might be convertible with copyOf.
    ImmutableList<ExecutableElement> copyOfMethods = copyOfMethods(targetType, nullableParameter);
    if (!copyOfMethods.isEmpty()) {
      return getConvertingSetterFunction(copyOfMethods, propertyElement, setter, parameterType);
    }
    errorReporter.reportError(
        setter,
        "[%sGetVsSet] Parameter type %s of setter method should be %s to match %s",
        autoWhat(),
        parameterType,
        targetType,
        propertyString(propertyElement));
    return Optional.empty();
  }
Returns an {@code Optional} describing how to convert a value from the setter's parameter type
to the getter's return type, or {@code Optional.empty()} if the conversion isn't possible. An
error will have been reported in the latter case. We can convert if they are already the same
type, when the returned function will be the identity; or if the setter type can be copied
using a method like {@code ImmutableList.copyOf} or {@code Optional.of}, when the returned
function will be something like {@code s -> "Optional.of(" + s + ")"}.
value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java