Skip to content

Commit 99ae134

Browse files
committed
Add a propertyTypes() method to AutoValueExtension.Context, to allow extensions to see the true type of every property. Use it in the @memoized extension.
RELNOTES=Add a propertyTypes() method to AutoValueExtension.Context, to allow extensions to see the true type of every property. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=267606097
1 parent 5ea8ebd commit 99ae134

File tree

7 files changed

+163
-73
lines changed

7 files changed

+163
-73
lines changed

value/src/main/java/com/google/auto/value/extension/AutoValueExtension.java

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import javax.annotation.processing.SupportedOptions;
2424
import javax.lang.model.element.ExecutableElement;
2525
import javax.lang.model.element.TypeElement;
26+
import javax.lang.model.type.TypeMirror;
2627

2728
/**
2829
* An AutoValueExtension allows for extra functionality to be created during the generation of an
@@ -58,13 +59,13 @@
5859
* <p>The first generated class in the hierarchy will always be the one generated by the AutoValue
5960
* processor and the last one will always be the one generated by the Extension that {@code
6061
* mustBeFinal}, if any. Other than that, the order of the classes in the hierarchy is unspecified.
61-
* The * last class in the hierarchy is {@code AutoValue_Foo} and that is the one that the
62+
* The last class in the hierarchy is {@code AutoValue_Foo} and that is the one that the
6263
* {@code Foo} class will reference, for example with {@code new AutoValue_Foo(...)}.
6364
*
6465
* <p>Each Extension must also be sure to generate a constructor with arguments corresponding to all
65-
* properties in {@link com.google.auto.value.extension.AutoValueExtension.Context#properties()}, in
66-
* order, and to call the superclass constructor with the same arguments. This constructor must have
67-
* at least package visibility.
66+
* properties in {@link com.google.auto.value.extension.AutoValueExtension.Context#propertyTypes()},
67+
* in order, and to call the superclass constructor with the same arguments. This constructor must
68+
* have at least package visibility.
6869
*
6970
* <p>Because the class generated by the AutoValue processor is at the top of the generated
7071
* hierarchy, Extensions can override its methods, for example {@code hashCode()},
@@ -98,9 +99,42 @@ public interface Context {
9899
* example, if property {@code bar} is defined by {@code abstract String getBar()} then this map
99100
* will have an entry mapping {@code "bar"} to the {@code ExecutableElement} for {@code
100101
* getBar()}.
102+
*
103+
* <p>To determine the type of a property, it is best to use {@link #propertyTypes()} rather
104+
* than looking at the return type of the {@link ExecutableElement} in this map. The reason is
105+
* that the final type of the property might be different because of type variables. For
106+
* example, if you have...
107+
*
108+
* <pre>
109+
* interface Parent<T> {
110+
* T bar();
111+
* }
112+
*
113+
* {@code @AutoValue abstract class Foo implements Parent<String> {...}}
114+
* </pre>
115+
*
116+
* ...then the type of the {@code bar} property in {@code Foo} is actually {@code String}, but
117+
* the {@code ExecutableElement} will be the the method in {@code Parent}, whose return type is
118+
* {@code T}.
101119
*/
102120
Map<String, ExecutableElement> properties();
103121

122+
/**
123+
* Returns the properties to be generated by AutoValue, with their types. Each key is a property
124+
* name, and the corresponding value is the type of that property. The order of the map entries
125+
* is the same as the order of the {@code @AutoValue} properties.
126+
*
127+
* <p>For example, if property {@code bar} is defined by {@code abstract String getBar()} then
128+
* this map will have an entry mapping {@code "bar"} to the {@code TypeMirror} for {@code
129+
* String}.
130+
*
131+
* <p>For compatibility reasons, this method has a default implementation that throws an
132+
* exception. The AutoValue processor supplies an implementation that behaves as documented.
133+
*/
134+
default Map<String, TypeMirror> propertyTypes() {
135+
throw new UnsupportedOperationException();
136+
}
137+
104138
/**
105139
* Returns the complete set of abstract methods defined in or inherited by the
106140
* {@code @AutoValue} class. This includes all methods that define properties (like {@code
@@ -269,13 +303,20 @@ public Set<ExecutableElement> consumeMethods(Context context) {
269303
* <pre>{@code
270304
* package <package>;
271305
* ...
272-
* <finalOrAbstract> class <className> extends <classToExtend> {...}
306+
* <finalOrAbstract> class <className> extends <classToExtend> {
307+
* // Constructor
308+
* <className>(<constructorParameters>) {
309+
* super(<constructorParameterNames>);
310+
* ...
311+
* }
312+
* ...
273313
* }</pre>
274314
*
275315
* <p>Here, {@code <package>} is {@link Context#packageName()}; {@code <finalOrAbstract>} is the
276316
* keyword {@code final} if {@code isFinal} is true or {@code abstract} otherwise; and {@code
277317
* <className>} and {@code <classToExtend>} are the values of this method's parameters of the same
278-
* name.
318+
* name. The {@code <constructorParameters>} and {@code <constructorParameterNames>} are typically
319+
* derived from {@link Context#propertyTypes()}.
279320
*
280321
* @param context The {@link Context} of the code generation for this class.
281322
* @param className The simple name of the resulting class. The returned code will be written to a

value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import com.google.common.base.Joiner;
5454
import com.google.common.collect.ImmutableList;
5555
import com.google.common.collect.ImmutableSet;
56+
import com.google.errorprone.annotations.FormatMethod;
5657
import com.squareup.javapoet.AnnotationSpec;
5758
import com.squareup.javapoet.ClassName;
5859
import com.squareup.javapoet.CodeBlock;
@@ -198,8 +199,8 @@ private ImmutableList<TypeVariableName> typeVariableNames() {
198199

199200
private MethodSpec constructor() {
200201
MethodSpec.Builder constructor = constructorBuilder();
201-
for (Map.Entry<String, ExecutableElement> property : context.properties().entrySet()) {
202-
constructor.addParameter(annotatedReturnType(property.getValue()), property.getKey() + "$");
202+
for (Map.Entry<String, TypeMirror> property : context.propertyTypes().entrySet()) {
203+
constructor.addParameter(annotatedType(property.getValue()), property.getKey() + "$");
203204
}
204205
List<String> namesWithDollars = new ArrayList<String>();
205206
for (String property : context.properties().keySet()) {
@@ -399,7 +400,8 @@ private final class MethodOverrider {
399400
this.method = method;
400401
validate();
401402
cacheField =
402-
buildCacheField(annotatedReturnType(method), method.getSimpleName().toString());
403+
buildCacheField(
404+
annotatedType(method.getReturnType()), method.getSimpleName().toString());
403405
fields.add(cacheField);
404406
override =
405407
methodBuilder(method.getSimpleName().toString())
@@ -457,10 +459,11 @@ private void validate() {
457459

458460
private void checkIllegalModifier(Modifier modifier) {
459461
if (method.getModifiers().contains(modifier)) {
460-
printMessage(ERROR, "@Memoized methods cannot be " + modifier.toString());
462+
printMessage(ERROR, "@Memoized methods cannot be %s", modifier.toString());
461463
}
462464
}
463465

466+
@FormatMethod
464467
private void printMessage(Kind kind, String format, Object... args) {
465468
if (kind.equals(ERROR)) {
466469
hasErrors = true;
@@ -585,13 +588,12 @@ private static boolean containsNullable(List<? extends AnnotationMirror> annotat
585588
.anyMatch(n -> n.contentEquals("Nullable"));
586589
}
587590

588-
/** The return type of the given method, including type annotations. */
589-
private static TypeName annotatedReturnType(ExecutableElement method) {
590-
TypeMirror returnType = method.getReturnType();
591+
/** Translate a {@link TypeMirror} into a {@link TypeName}, including type annotations. */
592+
private static TypeName annotatedType(TypeMirror type) {
591593
List<AnnotationSpec> annotations =
592-
returnType.getAnnotationMirrors().stream()
594+
type.getAnnotationMirrors().stream()
593595
.map(AnnotationSpec::get)
594596
.collect(toList());
595-
return TypeName.get(returnType).annotated(annotations);
597+
return TypeName.get(type).annotated(annotations);
596598
}
597599
}

value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,11 @@ void processType(TypeElement autoOneOfType) {
9999
Set<ExecutableElement> otherMethods = new LinkedHashSet<>(abstractMethods);
100100
otherMethods.remove(kindGetter);
101101

102-
ImmutableSet<ExecutableElement> propertyMethods = propertyMethodsIn(otherMethods);
103-
ImmutableBiMap<String, ExecutableElement> properties = propertyNameToMethodMap(propertyMethods);
104-
validateMethods(autoOneOfType, abstractMethods, propertyMethods, kindGetter);
102+
ImmutableMap<ExecutableElement, TypeMirror> propertyMethodsAndTypes =
103+
propertyMethodsIn(otherMethods, autoOneOfType);
104+
ImmutableBiMap<String, ExecutableElement> properties =
105+
propertyNameToMethodMap(propertyMethodsAndTypes.keySet());
106+
validateMethods(autoOneOfType, abstractMethods, propertyMethodsAndTypes.keySet(), kindGetter);
105107
ImmutableMap<String, String> propertyToKind =
106108
propertyToKindMap(kindMirror, properties.keySet());
107109

@@ -110,7 +112,7 @@ void processType(TypeElement autoOneOfType) {
110112
vars.generatedClass = TypeSimplifier.simpleNameOf(subclass);
111113
vars.propertyToKind = propertyToKind;
112114
defineSharedVarsForType(autoOneOfType, methods, vars);
113-
defineVarsForType(autoOneOfType, vars, propertyMethods, kindGetter);
115+
defineVarsForType(autoOneOfType, vars, propertyMethodsAndTypes, kindGetter);
114116

115117
String text = vars.toText();
116118
text = TypeEncoder.decode(text, processingEnv, vars.pkg, autoOneOfType.asType());
@@ -244,7 +246,7 @@ && objectMethodToOverride(method) == ObjectMethod.NONE) {
244246
// implement this alien method.
245247
errorReporter()
246248
.reportWarning(
247-
"Abstract methods in @AutoOneOf classes must be non-void with no parameters",
249+
"Abstract methods in @AutoOneOf classes must have no parameters",
248250
method);
249251
}
250252
}
@@ -254,10 +256,10 @@ && objectMethodToOverride(method) == ObjectMethod.NONE) {
254256
private void defineVarsForType(
255257
TypeElement type,
256258
AutoOneOfTemplateVars vars,
257-
ImmutableSet<ExecutableElement> propertyMethods,
259+
ImmutableMap<ExecutableElement, TypeMirror> propertyMethodsAndTypes,
258260
ExecutableElement kindGetter) {
259-
vars.props =
260-
propertySet(type, propertyMethods, ImmutableListMultimap.of(), ImmutableListMultimap.of());
261+
vars.props = propertySet(
262+
propertyMethodsAndTypes, ImmutableListMultimap.of(), ImmutableListMultimap.of());
261263
vars.kindGetter = kindGetter.getSimpleName().toString();
262264
vars.kindType = TypeEncoder.encode(kindGetter.getReturnType());
263265
TypeElement javaIoSerializable = elementUtils().getTypeElement("java.io.Serializable");

value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -359,46 +359,41 @@ public final boolean process(Set<? extends TypeElement> annotations, RoundEnviro
359359
* AutoValue.CopyAnnotations} also do not appear here.
360360
*/
361361
final ImmutableSet<Property> propertySet(
362-
TypeElement type,
363-
ImmutableSet<ExecutableElement> propertyMethods,
362+
ImmutableMap<ExecutableElement, TypeMirror> propertyMethodsAndTypes,
364363
ImmutableListMultimap<ExecutableElement, AnnotationMirror> annotatedPropertyFields,
365364
ImmutableListMultimap<ExecutableElement, AnnotationMirror> annotatedPropertyMethods) {
366365
ImmutableBiMap<ExecutableElement, String> methodToPropertyName =
367-
propertyNameToMethodMap(propertyMethods).inverse();
366+
propertyNameToMethodMap(propertyMethodsAndTypes.keySet()).inverse();
368367
Map<ExecutableElement, String> methodToIdentifier = new LinkedHashMap<>(methodToPropertyName);
369368
fixReservedIdentifiers(methodToIdentifier);
370-
EclipseHack eclipseHack = new EclipseHack(processingEnv);
371-
DeclaredType declaredType = MoreTypes.asDeclared(type.asType());
372-
ImmutableMap<ExecutableElement, TypeMirror> returnTypes =
373-
eclipseHack.methodReturnTypes(propertyMethods, declaredType);
374369

375370
ImmutableSet.Builder<Property> props = ImmutableSet.builder();
376-
for (ExecutableElement propertyMethod : propertyMethods) {
377-
TypeMirror returnType = returnTypes.get(propertyMethod);
378-
String propertyType =
379-
TypeEncoder.encodeWithAnnotations(returnType, getExcludedAnnotationTypes(propertyMethod));
380-
String propertyName = methodToPropertyName.get(propertyMethod);
381-
String identifier = methodToIdentifier.get(propertyMethod);
382-
ImmutableList<String> fieldAnnotations =
383-
annotationStrings(annotatedPropertyFields.get(propertyMethod));
384-
ImmutableList<AnnotationMirror> methodAnnotationMirrors =
385-
annotatedPropertyMethods.get(propertyMethod);
386-
ImmutableList<String> methodAnnotations = annotationStrings(methodAnnotationMirrors);
387-
Optional<String> nullableAnnotation = nullableAnnotationForMethod(propertyMethod);
388-
Property p =
389-
new Property(
390-
propertyName,
391-
identifier,
392-
propertyMethod,
393-
propertyType,
394-
fieldAnnotations,
395-
methodAnnotations,
396-
nullableAnnotation);
397-
props.add(p);
398-
if (p.isNullable() && returnType.getKind().isPrimitive()) {
399-
errorReporter().reportError("Primitive types cannot be @Nullable", propertyMethod);
400-
}
401-
}
371+
propertyMethodsAndTypes.forEach(
372+
(propertyMethod, returnType) -> {
373+
String propertyType = TypeEncoder.encodeWithAnnotations(
374+
returnType, getExcludedAnnotationTypes(propertyMethod));
375+
String propertyName = methodToPropertyName.get(propertyMethod);
376+
String identifier = methodToIdentifier.get(propertyMethod);
377+
ImmutableList<String> fieldAnnotations =
378+
annotationStrings(annotatedPropertyFields.get(propertyMethod));
379+
ImmutableList<AnnotationMirror> methodAnnotationMirrors =
380+
annotatedPropertyMethods.get(propertyMethod);
381+
ImmutableList<String> methodAnnotations = annotationStrings(methodAnnotationMirrors);
382+
Optional<String> nullableAnnotation = nullableAnnotationForMethod(propertyMethod);
383+
Property p =
384+
new Property(
385+
propertyName,
386+
identifier,
387+
propertyMethod,
388+
propertyType,
389+
fieldAnnotations,
390+
methodAnnotations,
391+
nullableAnnotation);
392+
props.add(p);
393+
if (p.isNullable() && returnType.getKind().isPrimitive()) {
394+
errorReporter().reportError("Primitive types cannot be @Nullable", propertyMethod);
395+
}
396+
});
402397
return props.build();
403398
}
404399

@@ -716,10 +711,14 @@ static ImmutableSet<ExecutableElement> abstractMethodsIn(
716711
}
717712

718713
/**
719-
* Returns the subset of property methods in the given set of abstract methods. A property method
720-
* has no arguments, is not void, and is not {@code hashCode()} or {@code toString()}.
714+
* Returns the subset of property methods in the given set of abstract methods, with their actual
715+
* return types. A property method has no arguments, is not void, and is not {@code hashCode()} or
716+
* {@code toString()}.
721717
*/
722-
ImmutableSet<ExecutableElement> propertyMethodsIn(Set<ExecutableElement> abstractMethods) {
718+
ImmutableMap<ExecutableElement, TypeMirror> propertyMethodsIn(
719+
Set<ExecutableElement> abstractMethods,
720+
TypeElement autoValueOrOneOfType) {
721+
DeclaredType declaredType = MoreTypes.asDeclared(autoValueOrOneOfType.asType());
723722
ImmutableSet.Builder<ExecutableElement> properties = ImmutableSet.builder();
724723
for (ExecutableElement method : abstractMethods) {
725724
if (method.getParameters().isEmpty()
@@ -728,7 +727,7 @@ && objectMethodToOverride(method) == ObjectMethod.NONE) {
728727
properties.add(method);
729728
}
730729
}
731-
return properties.build();
730+
return new EclipseHack(processingEnv).methodReturnTypes(properties.build(), declaredType);
732731
}
733732

734733
/** True if void properties are allowed. */

0 commit comments

Comments
 (0)