diff options
author | Éamonn McManus <emcmanus@google.com> | 2021-04-08 09:13:55 -0700 |
---|---|---|
committer | Google Java Core Libraries <java-libraries-firehose+copybara@google.com> | 2021-04-08 09:14:28 -0700 |
commit | ea0a418bed3a099c989c41f278a6937b1de9f4e2 (patch) | |
tree | d03a810e9aa8349ec3669c3647c24fafc672b981 /value/src/main/java/com/google/auto | |
parent | b8257718e73a2b051a67a96626d9785f353bf34a (diff) | |
download | auto-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')
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 |