Optional<PropertyBuilder> makePropertyBuilder(ExecutableElement method, String property) {
    AnnotatedTypeMirror barBuilderAnnotatedType =
        builderMethodClassifier.builderMethodReturnType(method);
    if (barBuilderAnnotatedType.getKind() != TypeKind.DECLARED) {
      errorReporter.reportError(
          method,
          "[AutoValueOddBuilderMethod] Method looks like a property builder, but its return type"
              + " is not a class or interface");
      return Optional.empty();
    }
    DeclaredType barBuilderDeclaredType = MoreTypes.asDeclared(barBuilderAnnotatedType.getType());
    TypeElement barBuilderTypeElement = MoreTypes.asTypeElement(barBuilderDeclaredType);
    Map<String, ExecutableElement> barBuilderNoArgMethods = noArgMethodsOf(barBuilderTypeElement);

    TypeMirror barTypeMirror = propertyTypes.get(property).getType();
    if (barTypeMirror.getKind() != TypeKind.DECLARED) {
      errorReporter.reportError(
          method,
          "[AutoValueBadBuilderMethod] Method looks like a property builder, but the type of"
              + " property %s is not a class or interface",
          property);
      return Optional.empty();
    }
    if (propertyIsNullable.test(property)) {
      errorReporter.reportError(
          method,
          "[AutoValueNullBuilder] Property %s is @Nullable so it cannot have a property builder",
          property);
    }
    TypeElement barTypeElement = MoreTypes.asTypeElement(barTypeMirror);
    Map<String, ExecutableElement> barNoArgMethods = noArgMethodsOf(barTypeElement);

    // Condition (1), must have build() or buildOrThrow() method returning Bar.
    ExecutableElement build = barBuilderNoArgMethods.get("buildOrThrow");
    if (build == null) {
      build = barBuilderNoArgMethods.get("build");
    }
    if (build == null || build.getModifiers().contains(Modifier.STATIC)) {
      errorReporter.reportError(
          method,
          "[AutoValueBuilderNotBuildable] Method looks like a property builder, but it returns %s"
              + " which does not have a non-static build() or buildOrThrow() method",
          barBuilderTypeElement);
      return Optional.empty();
    }

    // We've determined that `BarBuilder` has a method `build()` or `buildOrThrow(). But it must
    // return `Bar`. And if the type of `bar()` is Bar<String> then `BarBuilder.build()` must return
    // something that can be assigned to Bar<String>.
    TypeMirror buildType =
        MethodSignature.asMemberOf(typeUtils, barBuilderDeclaredType, build).returnType().getType();
    if (!typeUtils.isAssignable(buildType, barTypeMirror)) {
      errorReporter.reportError(
          method,
          "[AutoValueBuilderWrongType] Property builder for %s has type %s whose %s() method"
              + " returns %s instead of %s",
          property,
          barBuilderTypeElement,
          build.getSimpleName(),
          buildType,
          barTypeMirror);
      return Optional.empty();
    }

    Optional<ExecutableElement> maybeBuilderMaker;
    if (method.getParameters().isEmpty()) {
      maybeBuilderMaker = noArgBuilderMaker(barNoArgMethods, barBuilderTypeElement);
    } else {
      Map<String, ExecutableElement> barOneArgMethods = oneArgumentMethodsOf(barTypeElement);
      maybeBuilderMaker = oneArgBuilderMaker(barOneArgMethods, barBuilderTypeElement);
    }
    if (!maybeBuilderMaker.isPresent()) {
      errorReporter.reportError(
          method,
          "[AutoValueCantMakePropertyBuilder] Method looks like a property builder, but its type"
              + " %s does not have a public constructor and %s does not have a static builder() or"
              + " newBuilder() method that returns %s",
          barBuilderTypeElement,
          barTypeElement,
          barBuilderTypeElement);
      return Optional.empty();
    }
    ExecutableElement builderMaker = maybeBuilderMaker.get();

    String barBuilderType = TypeEncoder.encodeWithAnnotations(barBuilderAnnotatedType);
    String nullableBarBuilderType =
        TypeEncoder.encodeWithAnnotations(
            barBuilderAnnotatedType, nullables.nullableTypeAnnotations());
    String rawBarType = TypeEncoder.encodeRaw(barTypeMirror);
    String arguments =
        method.getParameters().isEmpty()
            ? "()"
            : "(" + method.getParameters().get(0).getSimpleName() + ")";
    String initializer =
        (builderMaker.getKind() == ElementKind.CONSTRUCTOR)
            ? "new " + barBuilderType + arguments
            : rawBarType + "." + builderMaker.getSimpleName() + arguments;
    String builtToBuilder = null;
    String copyAll = null;
    ExecutableElement toBuilder = barNoArgMethods.get("toBuilder");
    if (toBuilder != null
        && !toBuilder.getModifiers().contains(Modifier.STATIC)
        && typeUtils.isAssignable(
            typeUtils.erasure(toBuilder.getReturnType()),
            typeUtils.erasure(barBuilderDeclaredType))) {
      builtToBuilder = toBuilder.getSimpleName().toString();
    } else {
      Optional<ExecutableElement> maybeCopyAll =
          addAllPutAll(barBuilderTypeElement, barBuilderDeclaredType, barTypeMirror);
      if (maybeCopyAll.isPresent()) {
        copyAll = maybeCopyAll.get().getSimpleName().toString();
      }
    }
    ExecutableElement barOf = barNoArgMethods.get("of");
    boolean hasOf = (barOf != null && barOf.getModifiers().contains(Modifier.STATIC));
    // An expression (initDefault) to make a default one of these, plus optionally a statement
    // (beforeInitDefault) that prepares the expression. For a collection, beforeInitDefault is
    // empty and initDefault is (e.g.) `ImmutableList.of()`. For a nested value type,
    // beforeInitDefault is (e.g.)
    //   `NestedAutoValueType.Builder foo$builder = NestedAutoValueType.builder();`
    // and initDefault is `foo$builder.build();`. The reason for the separate statement is to
    // exploit type inference rather than having to write `NestedAutoValueType.<Bar>build();`.
    String beforeInitDefault;
    String initDefault;
    if (hasOf) {
      beforeInitDefault = "";
      initDefault = rawBarType + ".of()";
    } else {
      String localBuilder = property + "$builder";
      beforeInitDefault = nullableBarBuilderType + " " + localBuilder + " = " + initializer + ";";
      initDefault = localBuilder + "." + build.getSimpleName() + "()";
    }

    PropertyBuilder propertyBuilder =
        new PropertyBuilder(
            method,
            barBuilderType,
            nullableBarBuilderType,
            barBuilderAnnotatedType,
            build.getSimpleName().toString(),
            initializer,
            beforeInitDefault,
            initDefault,
            builtToBuilder,
            copyAll);
    return Optional.of(propertyBuilder);
  }
The method to copy another collection into this builder. It is {@code addAll} for
one-dimensional collections like {@code ImmutableList} and {@code ImmutableSet}, and it is
{@code putAll} for two-dimensional collections like {@code ImmutableMap} and {@code
ImmutableTable}.
value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java