aboutsummaryrefslogtreecommitdiff
path: root/value/src/main/java/com/google/auto
diff options
context:
space:
mode:
authorÉamonn McManus <emcmanus@google.com>2021-04-08 09:13:55 -0700
committerGoogle Java Core Libraries <java-libraries-firehose+copybara@google.com>2021-04-08 09:14:28 -0700
commitea0a418bed3a099c989c41f278a6937b1de9f4e2 (patch)
treed03a810e9aa8349ec3669c3647c24fafc672b981 /value/src/main/java/com/google/auto
parentb8257718e73a2b051a67a96626d9785f353bf34a (diff)
downloadauto-ea0a418bed3a099c989c41f278a6937b1de9f4e2.tar.gz
Allow an AutoBuilder builder to call static methods.
Up until now, `@AutoBuilder` builders, like `@AutoValue.Builder` builders, only ever invoked constructors from their `build()` method. But AutoBuilder is specified to apply also to static methods. This introduces a new distinction which we did not have before, between the type containing the executable to invoke (up until now always a constructor) and the type that that executable produces (which for a constructor was obviously the same type). RELNOTES=n/a PiperOrigin-RevId: 367442955
Diffstat (limited to 'value/src/main/java/com/google/auto')
-rw-r--r--value/src/main/java/com/google/auto/value/AutoBuilder.java16
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java171
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueOrBuilderTemplateVars.java17
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java7
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java2
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java34
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java10
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java6
-rw-r--r--value/src/main/java/com/google/auto/value/processor/GwtSerialization.java6
-rw-r--r--value/src/main/java/com/google/auto/value/processor/builder.vm4
10 files changed, 171 insertions, 102 deletions
diff --git a/value/src/main/java/com/google/auto/value/AutoBuilder.java b/value/src/main/java/com/google/auto/value/AutoBuilder.java
index 55b86235..481f6dc7 100644
--- a/value/src/main/java/com/google/auto/value/AutoBuilder.java
+++ b/value/src/main/java/com/google/auto/value/AutoBuilder.java
@@ -44,7 +44,19 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface AutoBuilder {
- Class<?> ofClass() default Void.class;
+ /**
+ * The static method from {@link #ofClass} to call when the build-method of the builder is called.
+ * By default this is empty, meaning that a constructor rather than a static method should be
+ * called. There can be more than one method with the given name, or more than one constructor, in
+ * which case the one to call is the one whose parameter names and types correspond to the
+ * abstract methods of the class or interface with the {@code @AutoBuilder} annotation.
+ */
+ String callMethod() default "";
- // TODO(b/183005059): support calling static methods as well as constructors.
+ /**
+ * The class or interface containing the constructor or static method that the generated builder
+ * will eventually call. By default this is the class or interface that <i>contains</i> the class
+ * or interface with the {@code @AutoBuilder} annotation.
+ */
+ Class<?> ofClass() default Void.class;
}
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java
index f9fa4159..696c9c5d 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java
@@ -24,14 +24,17 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toCollection;
import static javax.lang.model.util.ElementFilter.constructorsIn;
+import static javax.lang.model.util.ElementFilter.methodsIn;
import com.google.auto.common.AnnotationMirrors;
+import com.google.auto.common.AnnotationValues;
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.auto.common.Visibility;
import com.google.auto.service.AutoService;
import com.google.auto.value.processor.MissingTypes.MissingTypeException;
import com.google.common.base.Ascii;
+import com.google.common.base.VerifyException;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.lang.reflect.Field;
@@ -40,6 +43,7 @@ import java.util.NavigableSet;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
+import java.util.stream.Stream;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.SupportedAnnotationTypes;
@@ -48,6 +52,7 @@ import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
@@ -101,39 +106,45 @@ public class AutoBuilderProcessor extends AutoValueishProcessor {
"[AutoBuilderWrongType] @AutoBuilder only applies to classes and interfaces");
}
checkModifiersIfNested(autoBuilderType);
- TypeElement constructedType = findConstructedType(autoBuilderType);
- checkModifiersIfNested(constructedType); // TODO: error message is wrong
+ // The annotation is guaranteed to be present by the contract of Processor#process
+ AnnotationMirror autoBuilderAnnotation =
+ getAnnotationMirror(autoBuilderType, AUTO_BUILDER_NAME).get();
+ TypeElement ofClass = getOfClass(autoBuilderType, autoBuilderAnnotation);
+ String callMethod = findCallMethodValue(autoBuilderAnnotation);
+ checkModifiersIfNested(ofClass); // TODO: error message is wrong
ImmutableSet<ExecutableElement> methods =
abstractMethodsIn(
getLocalAndInheritedMethods(autoBuilderType, typeUtils(), elementUtils()));
- ExecutableElement constructor = findConstructor(constructedType, autoBuilderType, methods);
- BuilderSpec builderSpec = new BuilderSpec(constructedType, processingEnv, errorReporter());
+ ExecutableElement executable = findExecutable(ofClass, callMethod, autoBuilderType, methods);
+ BuilderSpec builderSpec = new BuilderSpec(ofClass, processingEnv, errorReporter());
BuilderSpec.Builder builder = builderSpec.new Builder(autoBuilderType);
+ TypeMirror builtType = builtType(executable);
Optional<BuilderMethodClassifier<VariableElement>> classifier =
BuilderMethodClassifierForAutoBuilder.classify(
- methods, errorReporter(), processingEnv, constructor, constructedType, autoBuilderType);
+ methods, errorReporter(), processingEnv, executable, builtType, autoBuilderType);
if (!classifier.isPresent()) {
// We've already output one or more error messages.
return;
}
AutoBuilderTemplateVars vars = new AutoBuilderTemplateVars();
- vars.props = propertySet(constructor);
+ vars.props = propertySet(executable);
builder.defineVars(vars, classifier.get());
vars.identifiers = !processingEnv.getOptions().containsKey(OMIT_IDENTIFIERS_OPTION);
String generatedClassName = generatedClassName(autoBuilderType, "AutoBuilder_");
vars.builderName = TypeSimplifier.simpleNameOf(generatedClassName);
- vars.builtClass = TypeEncoder.encodeRaw(constructedType.asType());
+ vars.builtType = TypeEncoder.encode(builtType);
+ vars.build = build(executable);
vars.types = typeUtils();
vars.toBuilderConstructor = false;
- defineSharedVarsForType(constructedType, ImmutableSet.of(), vars);
+ defineSharedVarsForType(ofClass, ImmutableSet.of(), vars);
String text = vars.toText();
text = TypeEncoder.decode(text, processingEnv, vars.pkg, autoBuilderType.asType());
text = Reformatter.fixup(text);
writeSourceFile(generatedClassName, text, autoBuilderType);
}
- private ImmutableSet<Property> propertySet(ExecutableElement constructor) {
- return constructor.getParameters().stream().map(this::newProperty).collect(toImmutableSet());
+ private ImmutableSet<Property> propertySet(ExecutableElement executable) {
+ return executable.getParameters().stream().map(this::newProperty).collect(toImmutableSet());
}
private Property newProperty(VariableElement var) {
@@ -142,57 +153,68 @@ public class AutoBuilderProcessor extends AutoValueishProcessor {
return new Property(name, name, TypeEncoder.encode(type), type, Optional.empty());
}
- private ExecutableElement findConstructor(
- TypeElement constructedType,
+ private ExecutableElement findExecutable(
+ TypeElement ofClass,
+ String callMethod,
TypeElement autoBuilderType,
ImmutableSet<ExecutableElement> methods) {
- List<ExecutableElement> constructors = visibleConstructors(constructedType, autoBuilderType);
- switch (constructors.size()) {
+ List<ExecutableElement> executables =
+ findRelevantExecutables(ofClass, callMethod, autoBuilderType);
+ String description = callMethod.isEmpty() ? "constructor" : "static method named " + callMethod;
+ switch (executables.size()) {
case 0:
throw errorReporter()
.abortWithError(
autoBuilderType,
- "[AutoBuilderNoConstructor] No visible constructors for %s",
- constructedType);
+ "[AutoBuilderNoVisible] No visible %s for %s",
+ description,
+ ofClass);
case 1:
- return constructors.get(0);
+ return executables.get(0);
default:
- return matchingConstructor(autoBuilderType, constructors, methods);
+ return matchingExecutable(autoBuilderType, executables, methods, description);
}
}
- private ImmutableList<ExecutableElement> visibleConstructors(
- TypeElement constructedType, TypeElement autoBuilderType) {
- return constructorsIn(constructedType.getEnclosedElements()).stream()
+ private ImmutableList<ExecutableElement> findRelevantExecutables(
+ TypeElement ofClass, String callMethod, TypeElement autoBuilderType) {
+ List<? extends Element> elements = ofClass.getEnclosedElements();
+ Stream<ExecutableElement> relevantExecutables =
+ callMethod.isEmpty()
+ ? constructorsIn(elements).stream()
+ : methodsIn(elements).stream()
+ .filter(m -> m.getSimpleName().contentEquals(callMethod))
+ .filter(m -> m.getModifiers().contains(Modifier.STATIC));
+ return relevantExecutables
.filter(c -> visibleFrom(c, getPackage(autoBuilderType)))
.collect(toImmutableList());
}
- private ExecutableElement matchingConstructor(
+ private ExecutableElement matchingExecutable(
TypeElement autoBuilderType,
- List<ExecutableElement> constructors,
- ImmutableSet<ExecutableElement> methods) {
- // There's more than one visible constructor. We try to find the one that corresponds to the
- // methods in the @AutoBuilder interface. This is a bit approximate. We're basically just
- // looking for a constructor where all the parameter names correspond to setters or
- // property builders in the interface. We might find out after choosing one that it is wrong
- // for whatever reason (types don't match, spurious methods, etc). But it is likely that if
- // the names are all accounted for in the methods, and if there's no other matching constructor
- // with more parameters, then this is indeed the one we want. If we later get errors when we
- // try to analyze the interface in detail, those are probably legitimate errors and not because
- // we picked the wrong constructor.
+ List<ExecutableElement> executables,
+ ImmutableSet<ExecutableElement> methods,
+ String description) {
+ // There's more than one visible executable (constructor or method). We try to find the one that
+ // corresponds to the methods in the @AutoBuilder interface. This is a bit approximate. We're
+ // basically just looking for an executable where all the parameter names correspond to setters
+ // or property builders in the interface. We might find out after choosing one that it is wrong
+ // for whatever reason (types don't match, spurious methods, etc). But it is likely that if the
+ // names are all accounted for in the methods, and if there's no other matching executable with
+ // more parameters, then this is indeed the one we want. If we later get errors when we try to
+ // analyze the interface in detail, those are probably legitimate errors and not because we
+ // picked the wrong executable.
ImmutableList<ExecutableElement> matches =
- constructors.stream()
- .filter(c -> constructorMatches(c, methods))
- .collect(toImmutableList());
+ executables.stream().filter(x -> executableMatches(x, methods)).collect(toImmutableList());
switch (matches.size()) {
case 0:
throw errorReporter()
.abortWithError(
autoBuilderType,
"[AutoBuilderNoMatch] Property names do not correspond to the parameter names of"
- + " any constructor:\n%s",
- constructorListString(constructors));
+ + " any %s:\n%s",
+ description,
+ executableListString(executables));
case 1:
return matches.get(0);
default:
@@ -205,28 +227,33 @@ public class AutoBuilderProcessor extends AutoValueishProcessor {
throw errorReporter()
.abortWithError(
autoBuilderType,
- "[AutoBuilderAmbiguous] Property names correspond to more than one constructor:\n"
- + "%s",
- constructorListString(maxMatches));
+ "[AutoBuilderAmbiguous] Property names correspond to more than one %s:\n%s",
+ description,
+ executableListString(maxMatches));
}
return maxMatches.get(0);
}
- private String constructorListString(List<ExecutableElement> constructors) {
- return constructors.stream().map(this::constructorString).collect(joining("\n ", " ", ""));
+ private String executableListString(List<ExecutableElement> executables) {
+ return executables.stream().map(this::executableString).collect(joining("\n ", " ", ""));
}
- private String constructorString(ExecutableElement constructor) {
- return constructor.getParameters().stream()
- .map(v -> v.asType() + " " + v.getSimpleName())
- .collect(joining(", ", "(", ")"));
+ private String executableString(ExecutableElement executable) {
+ Element nameSource =
+ executable.getKind() == ElementKind.CONSTRUCTOR
+ ? executable.getEnclosingElement()
+ : executable;
+ return nameSource.getSimpleName()
+ + executable.getParameters().stream()
+ .map(v -> v.asType() + " " + v.getSimpleName())
+ .collect(joining(", ", "(", ")"));
}
- private boolean constructorMatches(
- ExecutableElement constructor, ImmutableSet<ExecutableElement> methods) {
+ private boolean executableMatches(
+ ExecutableElement executable, ImmutableSet<ExecutableElement> methods) {
// Start with the complete set of parameter names and remove them one by one as we find
// corresponding methods. We ignore case, under the assumption that it is unlikely that a case
- // difference is going to allow a constructor to match when another one is better.
+ // difference is going to allow a candidate to match when another one is better.
// A parameter named foo could be matched by methods like this:
// X foo(Y)
// X setFoo(Y)
@@ -236,7 +263,7 @@ public class AutoBuilderProcessor extends AutoValueishProcessor {
// BuilderMethodClassifier, but here we just require that there be at least one method with
// one of these shapes for foo.
NavigableSet<String> parameterNames =
- constructor.getParameters().stream()
+ executable.getParameters().stream()
.map(v -> v.getSimpleName().toString())
.collect(toCollection(() -> new TreeSet<>(String.CASE_INSENSITIVE_ORDER)));
for (ExecutableElement method : methods) {
@@ -274,6 +301,32 @@ public class AutoBuilderProcessor extends AutoValueishProcessor {
}
}
+ private TypeMirror builtType(ExecutableElement executable) {
+ switch (executable.getKind()) {
+ case CONSTRUCTOR:
+ return executable.getEnclosingElement().asType();
+ case METHOD:
+ return executable.getReturnType();
+ default:
+ throw new VerifyException("Unexpected executable kind " + executable.getKind());
+ }
+ }
+
+ private String build(ExecutableElement executable) {
+ TypeElement enclosing = MoreElements.asType(executable.getEnclosingElement());
+ String type = TypeEncoder.encodeRaw(enclosing.asType());
+ switch (executable.getKind()) {
+ case CONSTRUCTOR:
+ boolean generic = !enclosing.getTypeParameters().isEmpty();
+ String typeParams = generic ? "<>" : "";
+ return "new " + type + typeParams;
+ case METHOD:
+ return type + "." + executable.getSimpleName();
+ default:
+ throw new VerifyException("Unexpected executable kind " + executable.getKind());
+ }
+ }
+
private static final ElementKind ELEMENT_KIND_RECORD = elementKindRecord();
private static ElementKind elementKindRecord() {
@@ -286,8 +339,9 @@ public class AutoBuilderProcessor extends AutoValueishProcessor {
}
}
- private TypeElement findConstructedType(TypeElement autoBuilderType) {
- TypeElement ofClassValue = findOfClassValue(autoBuilderType);
+ private TypeElement getOfClass(
+ TypeElement autoBuilderType, AnnotationMirror autoBuilderAnnotation) {
+ TypeElement ofClassValue = findOfClassValue(autoBuilderAnnotation);
boolean isDefault = typeUtils().isSameType(ofClassValue.asType(), javaLangVoid);
if (!isDefault) {
return ofClassValue;
@@ -306,10 +360,7 @@ public class AutoBuilderProcessor extends AutoValueishProcessor {
return MoreElements.asType(enclosing);
}
- private TypeElement findOfClassValue(TypeElement autoBuilderType) {
- // The annotation is guaranteed to be present by the contract of Processor#process
- AnnotationMirror autoBuilderAnnotation =
- getAnnotationMirror(autoBuilderType, AUTO_BUILDER_NAME).get();
+ private TypeElement findOfClassValue(AnnotationMirror autoBuilderAnnotation) {
AnnotationValue ofClassValue =
AnnotationMirrors.getAnnotationValue(autoBuilderAnnotation, "ofClass");
Object value = ofClassValue.getValue();
@@ -327,6 +378,12 @@ public class AutoBuilderProcessor extends AutoValueishProcessor {
throw new MissingTypeException(null);
}
+ private String findCallMethodValue(AnnotationMirror autoBuilderAnnotation) {
+ AnnotationValue callMethodValue =
+ AnnotationMirrors.getAnnotationValue(autoBuilderAnnotation, "callMethod");
+ return AnnotationValues.getString(callMethodValue);
+ }
+
@Override
Optional<String> nullableAnnotationForMethod(ExecutableElement propertyMethod) {
// TODO(b/183005059): implement
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueOrBuilderTemplateVars.java b/value/src/main/java/com/google/auto/value/processor/AutoValueOrBuilderTemplateVars.java
index f5be4f3f..83aab3ce 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoValueOrBuilderTemplateVars.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueOrBuilderTemplateVars.java
@@ -78,6 +78,15 @@ abstract class AutoValueOrBuilderTemplateVars extends AutoValueishTemplateVars {
/** The builder's build method, often {@code "build"}. */
Optional<SimpleMethod> buildMethod = Optional.empty();
+ /** The type that will be built by the {@code build()} method of a builder. */
+ String builtType;
+
+ /**
+ * The constructor or method invocation that the {@code build()} method of a builder should use,
+ * without any parameters. This might be {@code "new Foo"} or {@code "Foo.someMethod"}.
+ */
+ String build;
+
/**
* A multimap from property names (like foo) to the corresponding setters. The same property may
* be set by more than one setter. For example, an ImmutableList might be set by {@code
@@ -133,13 +142,7 @@ abstract class AutoValueOrBuilderTemplateVars extends AutoValueishTemplateVars {
* subclasses)
*/
Boolean isFinal = false;
-
+
/** The type utilities returned by {@link ProcessingEnvironment#getTypeUtils()}. */
Types types;
-
- /**
- * The simple name of the final generated subclass. For {@code @AutoValue public static class Foo
- * {}} this should always be "AutoValue_Foo".
- */
- String builtClass;
}
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java
index 0c1455cd..ef1f3098 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java
@@ -239,13 +239,14 @@ public class AutoValueProcessor extends AutoValueishProcessor {
boolean extensionsPresent = !applicableExtensions.isEmpty();
validateMethods(type, abstractMethods, toBuilderMethods, propertyMethods, extensionsPresent);
- String finalSubclass = generatedSubclassName(type, 0);
+ String finalSubclass = TypeSimplifier.simpleNameOf(generatedSubclassName(type, 0));
AutoValueTemplateVars vars = new AutoValueTemplateVars();
- vars.builtClass = TypeSimplifier.simpleNameOf(finalSubclass);
vars.types = processingEnv.getTypeUtils();
vars.identifiers = !processingEnv.getOptions().containsKey(OMIT_IDENTIFIERS_OPTION);
defineSharedVarsForType(type, methods, vars);
defineVarsForType(type, vars, toBuilderMethods, propertyMethodsAndTypes, builder);
+ vars.builtType = vars.origClass + vars.actualTypes;
+ vars.build = "new " + finalSubclass + vars.actualTypes;
// If we've encountered problems then we might end up invoking extensions with inconsistent
// state. Anyway we probably don't want to generate code which is likely to provoke further
@@ -267,7 +268,7 @@ public class AutoValueProcessor extends AutoValueishProcessor {
text = Reformatter.fixup(text);
writeSourceFile(subclass, text, type);
GwtSerialization gwtSerialization = new GwtSerialization(gwtCompatibility, processingEnv, type);
- gwtSerialization.maybeWriteGwtSerializer(vars);
+ gwtSerialization.maybeWriteGwtSerializer(vars, finalSubclass);
}
// Invokes each of the given extensions to generate its subclass, and returns the number of
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java
index 5c2977b2..a12792fa 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java
@@ -433,7 +433,7 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
return props.build();
}
- /** Defines the template variables that are shared by AutoValue and AutoOneOf. */
+ /** Defines the template variables that are shared by AutoValue, AutoOneOf, and AutoBuilder. */
final void defineSharedVarsForType(
TypeElement type,
ImmutableSet<ExecutableElement> methods,
diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java
index 54869957..28c98b25 100644
--- a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java
+++ b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java
@@ -67,7 +67,7 @@ abstract class BuilderMethodClassifier<E extends Element> {
private final ErrorReporter errorReporter;
private final Types typeUtils;
private final Elements elementUtils;
- private final TypeElement autoValueClass;
+ private final TypeMirror builtType;
private final TypeElement builderType;
/**
@@ -99,13 +99,13 @@ abstract class BuilderMethodClassifier<E extends Element> {
BuilderMethodClassifier(
ErrorReporter errorReporter,
ProcessingEnvironment processingEnv,
- TypeElement autoValueClass,
+ TypeMirror builtType,
TypeElement builderType,
ImmutableMap<String, TypeMirror> rewrittenPropertyTypes) {
this.errorReporter = errorReporter;
this.typeUtils = processingEnv.getTypeUtils();
this.elementUtils = processingEnv.getElementUtils();
- this.autoValueClass = autoValueClass;
+ this.builtType = builtType;
this.builderType = builderType;
this.rewrittenPropertyTypes = rewrittenPropertyTypes;
this.eclipseHack = new EclipseHack(processingEnv);
@@ -198,10 +198,9 @@ abstract class BuilderMethodClassifier<E extends Element> {
String setterName = settersPrefixed ? prefixWithSet(property) : property;
errorReporter.reportError(
builderType,
- "[AutoValueBuilderMissingMethod] Expected a method with this signature: %s%s"
+ "[AutoValueBuilderMissingMethod] Expected a method with this signature: %s"
+ " %s(%s), or a %sBuilder() method",
- builderType,
- typeParamsString(),
+ builderType.asType(),
setterName,
propertyType,
property);
@@ -264,16 +263,15 @@ abstract class BuilderMethodClassifier<E extends Element> {
}
}
- if (TYPE_EQUIVALENCE.equivalent(returnType, autoValueClass.asType())) {
+ if (TYPE_EQUIVALENCE.equivalent(returnType, builtType)) {
buildMethods.add(method);
} else {
errorReporter.reportError(
method,
"[AutoValueBuilderNoArg] Method without arguments should be a build method returning"
- + " %1$s%2$s, or a getter method with the same name and type as a getter method of"
+ + " %1$s, or a getter method with the same name and type as a getter method of"
+ " %1$s, or fooBuilder() where foo() or getFoo() is a getter method of %1$s",
- autoValueClass,
- typeParamsString());
+ builtType);
}
}
@@ -309,7 +307,7 @@ abstract class BuilderMethodClassifier<E extends Element> {
builderGetter,
"[AutoValueBuilderReturnType] Method matches a property of %1$s but has return type %2$s"
+ " instead of %3$s or an Optional wrapping of %3$s",
- autoValueClass,
+ builtType,
builderGetterType,
originalGetterType);
}
@@ -372,7 +370,7 @@ abstract class BuilderMethodClassifier<E extends Element> {
errorReporter.reportError(
method,
"[AutoValueBuilderWhatProp] Method does not correspond to a property of %s",
- autoValueClass);
+ builtType);
checkForFailedJavaBean(method);
return;
}
@@ -387,7 +385,7 @@ abstract class BuilderMethodClassifier<E extends Element> {
propertyName, new PropertySetter(method, parameterType, function.get()));
} else {
errorReporter.reportError(
- method, "Setter methods must return %s%s", builderType, typeParamsString());
+ method, "Setter methods must return %s", builderType.asType());
}
}
}
@@ -457,7 +455,7 @@ abstract class BuilderMethodClassifier<E extends Element> {
setter,
"[AutoValueNullNotNull] Parameter of setter method is @Nullable but property method"
+ " %s.%s() is not",
- autoValueClass,
+ typeUtils.erasure(builtType),
propertyElement.getSimpleName());
return Optional.empty();
}
@@ -475,7 +473,7 @@ abstract class BuilderMethodClassifier<E extends Element> {
"[AutoValueGetVsSet] Parameter type %s of setter method should be %s to match getter %s.%s",
parameterType,
targetType,
- autoValueClass,
+ typeUtils.erasure(builtType),
propertyElement.getSimpleName());
return Optional.empty();
}
@@ -506,7 +504,7 @@ abstract class BuilderMethodClassifier<E extends Element> {
+ " getter %s.%s, or it should be a type that can be passed to %s.%s to produce %s",
parameterType,
targetType,
- autoValueClass,
+ typeUtils.erasure(builtType),
propertyElement.getSimpleName(),
targetTypeSimpleName,
copyOfMethods.get(0).getSimpleName(),
@@ -642,10 +640,6 @@ abstract class BuilderMethodClassifier<E extends Element> {
return "set" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
}
- private String typeParamsString() {
- return TypeSimplifier.actualTypeParametersString(autoValueClass);
- }
-
/**
* True if the given property is nullable, either because its type has a {@code @Nullable} type
* annotation, or because its getter method has a {@code @Nullable} method annotation.
diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java
index 8ca14f59..a7d05419 100644
--- a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java
+++ b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java
@@ -34,14 +34,14 @@ class BuilderMethodClassifierForAutoBuilder extends BuilderMethodClassifier<Vari
private BuilderMethodClassifierForAutoBuilder(
ErrorReporter errorReporter,
ProcessingEnvironment processingEnv,
- TypeElement ofClass,
+ TypeMirror builtType,
TypeElement builderType,
ImmutableBiMap<VariableElement, String> paramToPropertyName,
ImmutableMap<String, TypeMirror> propertyTypes) {
super(
errorReporter,
processingEnv,
- ofClass,
+ builtType,
builderType,
propertyTypes);
this.paramToPropertyName = paramToPropertyName;
@@ -54,7 +54,7 @@ class BuilderMethodClassifierForAutoBuilder extends BuilderMethodClassifier<Vari
* @param errorReporter where to report errors.
* @param processingEnv the ProcessingEnvironment for annotation processing.
* @param callMethod the constructor or static method that AutoBuilder will call.
- * @param ofClass the class to be built.
+ * @param builtType the type to be built.
* @param builderType the builder class or interface within {@code ofClass}.
* @return an {@code Optional} that contains the results of the classification if it was
* successful or nothing if it was not.
@@ -64,7 +64,7 @@ class BuilderMethodClassifierForAutoBuilder extends BuilderMethodClassifier<Vari
ErrorReporter errorReporter,
ProcessingEnvironment processingEnv,
ExecutableElement callMethod,
- TypeElement ofClass,
+ TypeMirror builtType,
TypeElement builderType) {
ImmutableBiMap<VariableElement, String> paramToPropertyName =
callMethod.getParameters().stream()
@@ -76,7 +76,7 @@ class BuilderMethodClassifierForAutoBuilder extends BuilderMethodClassifier<Vari
new BuilderMethodClassifierForAutoBuilder(
errorReporter,
processingEnv,
- ofClass,
+ builtType,
builderType,
paramToPropertyName,
propertyTypes);
diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java
index 37d6fde5..a5ea1951 100644
--- a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java
+++ b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java
@@ -35,14 +35,14 @@ class BuilderMethodClassifierForAutoValue extends BuilderMethodClassifier<Execut
private BuilderMethodClassifierForAutoValue(
ErrorReporter errorReporter,
ProcessingEnvironment processingEnv,
- TypeElement autoValueClass,
+ TypeMirror builtType,
TypeElement builderType,
ImmutableBiMap<ExecutableElement, String> getterToPropertyName,
ImmutableMap<String, TypeMirror> rewrittenPropertyTypes) {
super(
errorReporter,
processingEnv,
- autoValueClass,
+ builtType,
builderType,
rewrittenPropertyTypes);
this.errorReporter = errorReporter;
@@ -81,7 +81,7 @@ class BuilderMethodClassifierForAutoValue extends BuilderMethodClassifier<Execut
new BuilderMethodClassifierForAutoValue(
errorReporter,
processingEnv,
- autoValueClass,
+ autoValueClass.asType(),
builderType,
getterToPropertyName,
rewrittenPropertyTypes);
diff --git a/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java b/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java
index cd94d3bb..8cd79433 100644
--- a/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java
+++ b/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java
@@ -81,12 +81,14 @@ class GwtSerialization {
* com.example.AutoValue_Foo_CustomFieldSerializer.
*
* @param autoVars the template variables defined for this type.
+ * @param finalSubclass the simple name of the AutoValue class being generated, AutoValue_Foo
+ * in the example.
*/
- void maybeWriteGwtSerializer(AutoValueTemplateVars autoVars) {
+ void maybeWriteGwtSerializer(AutoValueTemplateVars autoVars, String finalSubclass) {
if (shouldWriteGwtSerializer()) {
GwtTemplateVars vars = new GwtTemplateVars();
vars.pkg = autoVars.pkg;
- vars.subclass = autoVars.builtClass;
+ vars.subclass = finalSubclass;
vars.formalTypes = autoVars.formalTypes;
vars.actualTypes = autoVars.actualTypes;
vars.useBuilder = !autoVars.builderTypeName.isEmpty();
diff --git a/value/src/main/java/com/google/auto/value/processor/builder.vm b/value/src/main/java/com/google/auto/value/processor/builder.vm
index 13566bdb..0fa18c97 100644
--- a/value/src/main/java/com/google/auto/value/processor/builder.vm
+++ b/value/src/main/java/com/google/auto/value/processor/builder.vm
@@ -220,7 +220,7 @@ class ${builderName}${builderFormalTypes} ##
#end
@`java.lang.Override`
- ${buildMethod.get().access}${origClass}${actualTypes} ${buildMethod.get().name}() {
+ ${buildMethod.get().access}${builtType} ${buildMethod.get().name}() {
#foreach ($p in $props)
#set ($propertyBuilder = $builderPropertyBuilders[$p.name])
@@ -264,7 +264,7 @@ class ${builderName}${builderFormalTypes} ##
#end
#end
- return new ${builtClass}${actualTypes}(
+ return ${build}(
#foreach ($p in $props)
this.$p #if ($foreach.hasNext) , #end