• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.auto.value.processor;
17 
18 import static com.google.auto.value.processor.AutoValueishProcessor.nullableAnnotationFor;
19 
20 import com.google.auto.common.MoreElements;
21 import com.google.auto.common.MoreTypes;
22 import com.google.auto.value.processor.BuilderSpec.Copier;
23 import com.google.auto.value.processor.BuilderSpec.PropertySetter;
24 import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder;
25 import com.google.common.base.Equivalence;
26 import com.google.common.collect.ImmutableBiMap;
27 import com.google.common.collect.ImmutableList;
28 import com.google.common.collect.ImmutableMap;
29 import com.google.common.collect.ImmutableMultimap;
30 import com.google.common.collect.ImmutableSet;
31 import com.google.common.collect.Iterables;
32 import com.google.common.collect.LinkedListMultimap;
33 import com.google.common.collect.Multimap;
34 import java.util.LinkedHashMap;
35 import java.util.LinkedHashSet;
36 import java.util.Map;
37 import java.util.Optional;
38 import java.util.Set;
39 import java.util.function.Function;
40 import java.util.stream.Stream;
41 import javax.annotation.processing.ProcessingEnvironment;
42 import javax.lang.model.element.Element;
43 import javax.lang.model.element.ExecutableElement;
44 import javax.lang.model.element.Modifier;
45 import javax.lang.model.element.TypeElement;
46 import javax.lang.model.element.VariableElement;
47 import javax.lang.model.type.DeclaredType;
48 import javax.lang.model.type.ExecutableType;
49 import javax.lang.model.type.TypeKind;
50 import javax.lang.model.type.TypeMirror;
51 import javax.lang.model.util.ElementFilter;
52 import javax.lang.model.util.Elements;
53 import javax.lang.model.util.Types;
54 
55 /**
56  * Classifies methods inside builder types, based on their names and parameter and return types.
57  *
58  * @param <E> the kind of {@link Element} that the corresponding properties are defined by. This is
59  *     {@link ExecutableElement} for AutoValue, where properties are defined by abstract methods,
60  *     and {@link VariableElement} for AutoBuilder, where they are defined by constructor or method
61  *     parameters.
62  * @author Éamonn McManus
63  */
64 abstract class BuilderMethodClassifier<E extends Element> {
65   private static final Equivalence<TypeMirror> TYPE_EQUIVALENCE = MoreTypes.equivalence();
66 
67   private final ErrorReporter errorReporter;
68   private final Types typeUtils;
69   private final Elements elementUtils;
70   private final TypeMirror builtType;
71   private final TypeElement builderType;
72 
73   /**
74    * Property types, rewritten to refer to type variables in the builder. For example, suppose you
75    * have {@code @AutoValue abstract class Foo<T>} with a getter {@code abstract T bar()} and a
76    * builder {@code @AutoValue.Builder interface Builder<T>} with a setter {@code abstract
77    * Builder<T> setBar(T t)}. Then the {@code T} of {@code Foo<T>} and the {@code T} of {@code
78    * Foo.Builder<T>} are two separate variables. Originally {@code bar()} returned the {@code T} of
79    * {@code Foo<T>}, but in this map we have rewritten it to be the {@code T} of {@code
80    * Foo.Builder<T>}.
81    *
82    * <p>Importantly, this rewrite <b>loses type annotations</b>, so when those are important we must
83    * be careful to look at the original type as reported by the {@link #originalPropertyType}
84    * method.
85    */
86   private final ImmutableMap<String, TypeMirror> rewrittenPropertyTypes;
87 
88   private final Set<ExecutableElement> buildMethods = new LinkedHashSet<>();
89   private final Map<String, BuilderSpec.PropertyGetter> builderGetters = new LinkedHashMap<>();
90   private final Map<String, PropertyBuilder> propertyNameToPropertyBuilder = new LinkedHashMap<>();
91   private final Multimap<String, PropertySetter> propertyNameToPrefixedSetters =
92       LinkedListMultimap.create();
93   private final Multimap<String, PropertySetter> propertyNameToUnprefixedSetters =
94       LinkedListMultimap.create();
95   private final EclipseHack eclipseHack;
96 
97   private boolean settersPrefixed;
98 
BuilderMethodClassifier( ErrorReporter errorReporter, ProcessingEnvironment processingEnv, TypeMirror builtType, TypeElement builderType, ImmutableMap<String, TypeMirror> rewrittenPropertyTypes)99   BuilderMethodClassifier(
100       ErrorReporter errorReporter,
101       ProcessingEnvironment processingEnv,
102       TypeMirror builtType,
103       TypeElement builderType,
104       ImmutableMap<String, TypeMirror> rewrittenPropertyTypes) {
105     this.errorReporter = errorReporter;
106     this.typeUtils = processingEnv.getTypeUtils();
107     this.elementUtils = processingEnv.getElementUtils();
108     this.builtType = builtType;
109     this.builderType = builderType;
110     this.rewrittenPropertyTypes = rewrittenPropertyTypes;
111     this.eclipseHack = new EclipseHack(processingEnv);
112   }
113 
114   /**
115    * Returns a multimap from the name of a property to the methods that set it. If the property is
116    * defined by an abstract method in the {@code @AutoValue} class called {@code foo()} or {@code
117    * getFoo()} then the name of the property is {@code foo} and there will be an entry in the map
118    * where the key is {@code "foo"} and the value describes a method in the builder called {@code
119    * foo} or {@code setFoo}.
120    */
propertyNameToSetters()121   ImmutableMultimap<String, PropertySetter> propertyNameToSetters() {
122     return ImmutableMultimap.copyOf(
123         settersPrefixed ? propertyNameToPrefixedSetters : propertyNameToUnprefixedSetters);
124   }
125 
propertyNameToPropertyBuilder()126   Map<String, PropertyBuilder> propertyNameToPropertyBuilder() {
127     return propertyNameToPropertyBuilder;
128   }
129 
130   /**
131    * Returns the set of properties that have getters in the builder. If a property is defined by an
132    * abstract method in the {@code @AutoValue} class called {@code foo()} or {@code getFoo()} then
133    * the name of the property is {@code foo}, If the builder also has a method of the same name
134    * ({@code foo()} or {@code getFoo()}) then the set returned here will contain {@code foo}.
135    */
builderGetters()136   ImmutableMap<String, BuilderSpec.PropertyGetter> builderGetters() {
137     return ImmutableMap.copyOf(builderGetters);
138   }
139 
140   /**
141    * Returns the methods that were identified as {@code build()} methods. These are methods that
142    * have no parameters and return the {@code @AutoValue} type, conventionally called {@code
143    * build()}.
144    */
buildMethods()145   Set<ExecutableElement> buildMethods() {
146     return ImmutableSet.copyOf(buildMethods);
147   }
148 
149   /** Classifies the given methods and sets the state of this object based on what is found. */
classifyMethods(Iterable<ExecutableElement> methods, boolean autoValueHasToBuilder)150   boolean classifyMethods(Iterable<ExecutableElement> methods, boolean autoValueHasToBuilder) {
151     int startErrorCount = errorReporter.errorCount();
152     for (ExecutableElement method : methods) {
153       classifyMethod(method);
154     }
155     if (errorReporter.errorCount() > startErrorCount) {
156       return false;
157     }
158     Multimap<String, PropertySetter> propertyNameToSetter;
159     if (propertyNameToPrefixedSetters.isEmpty()) {
160       propertyNameToSetter = propertyNameToUnprefixedSetters;
161       this.settersPrefixed = false;
162     } else if (propertyNameToUnprefixedSetters.isEmpty()) {
163       propertyNameToSetter = propertyNameToPrefixedSetters;
164       this.settersPrefixed = true;
165     } else {
166       errorReporter.reportError(
167           propertyNameToUnprefixedSetters.values().iterator().next().getSetter(),
168           "[%sSetNotSet] If any setter methods use the setFoo convention then all must",
169           autoWhat());
170       return false;
171     }
172     for (String property : rewrittenPropertyTypes.keySet()) {
173       TypeMirror propertyType = rewrittenPropertyTypes.get(property);
174       boolean hasSetter = propertyNameToSetter.containsKey(property);
175       PropertyBuilder propertyBuilder = propertyNameToPropertyBuilder.get(property);
176       boolean hasBuilder = propertyBuilder != null;
177       if (hasBuilder) {
178         // If property bar of type Bar has a barBuilder() that returns BarBuilder, then it must
179         // be possible to make a BarBuilder from a Bar if either (1) the @AutoValue class has a
180         // toBuilder() or (2) there is also a setBar(Bar). Making BarBuilder from Bar is
181         // possible if Bar either has a toBuilder() method or BarBuilder has an addAll or putAll
182         // method that accepts a Bar argument.
183         boolean canMakeBarBuilder =
184             (propertyBuilder.getBuiltToBuilder() != null || propertyBuilder.getCopyAll() != null);
185         boolean needToMakeBarBuilder = (autoValueHasToBuilder || hasSetter);
186         if (needToMakeBarBuilder && !canMakeBarBuilder) {
187           errorReporter.reportError(
188               propertyBuilder.getPropertyBuilderMethod(),
189               "[AutoValueCantMakeBuilder] Property builder method returns %1$s but there is no"
190                   + " way to make that type from %2$s: %2$s does not have a non-static"
191                   + " toBuilder() method that returns %1$s, and %1$s does not have a method"
192                   + " addAll or putAll that accepts an argument of type %2$s",
193               propertyBuilder.getBuilderTypeMirror(),
194               propertyType);
195         }
196       } else if (!hasSetter) {
197         // We have neither barBuilder() nor setBar(Bar), so we should complain.
198         String setterName = settersPrefixed ? prefixWithSet(property) : property;
199         errorReporter.reportError(
200             builderType,
201             "[%sBuilderMissingMethod] Expected a method with this signature: %s"
202                 + " %s(%s), or a %sBuilder() method",
203             autoWhat(),
204             builderType.asType(),
205             setterName,
206             propertyType,
207             property);
208       }
209     }
210     return errorReporter.errorCount() == startErrorCount;
211   }
212 
213   /** Classifies a method and update the state of this object based on what is found. */
classifyMethod(ExecutableElement method)214   private void classifyMethod(ExecutableElement method) {
215     switch (method.getParameters().size()) {
216       case 0:
217         classifyMethodNoArgs(method);
218         break;
219       case 1:
220         classifyMethodOneArg(method);
221         break;
222       default:
223         errorReporter.reportError(
224             method, "[%sBuilderArgs] Builder methods must have 0 or 1 parameters", autoWhat());
225     }
226   }
227 
228   /**
229    * Classifies a method given that it has no arguments. Currently a method with no arguments can be
230    * a {@code build()} method, meaning that its return type must be the {@code @AutoValue} class; it
231    * can be a getter, with the same signature as one of the property getters in the
232    * {@code @AutoValue} class; or it can be a property builder, like {@code
233    * ImmutableList.Builder<String> foosBuilder()} for the property defined by {@code
234    * ImmutableList<String> foos()} or {@code getFoos()}.
235    */
classifyMethodNoArgs(ExecutableElement method)236   private void classifyMethodNoArgs(ExecutableElement method) {
237     Optional<String> getterProperty = propertyForBuilderGetter(method);
238     if (getterProperty.isPresent()) {
239       classifyGetter(method, getterProperty.get());
240       return;
241     }
242 
243     String methodName = method.getSimpleName().toString();
244     TypeMirror returnType = builderMethodReturnType(method);
245 
246     if (methodName.endsWith("Builder")) {
247       String property = methodName.substring(0, methodName.length() - "Builder".length());
248       if (rewrittenPropertyTypes.containsKey(property)) {
249         PropertyBuilderClassifier propertyBuilderClassifier =
250             new PropertyBuilderClassifier(
251                 errorReporter,
252                 typeUtils,
253                 elementUtils,
254                 this,
255                 this::propertyIsNullable,
256                 rewrittenPropertyTypes,
257                 eclipseHack);
258         Optional<PropertyBuilder> propertyBuilder =
259             propertyBuilderClassifier.makePropertyBuilder(method, property);
260         if (propertyBuilder.isPresent()) {
261           propertyNameToPropertyBuilder.put(property, propertyBuilder.get());
262         }
263         return;
264       }
265     }
266 
267     if (TYPE_EQUIVALENCE.equivalent(returnType, builtType)) {
268       buildMethods.add(method);
269     } else {
270       errorReporter.reportError(
271           method,
272           "[%1$sBuilderNoArg] Method without arguments should be a build method returning"
273               + " %2$s, or a getter method with the same name and type as %3$s,"
274               + " or fooBuilder() where %4$s is %3$s",
275           // "where foo() or getFoo() is a method in..." or "where foo is a parameter of..."
276           autoWhat(),
277           builtType,
278           getterMustMatch(),
279           fooBuilderMustMatch());
280     }
281   }
282 
classifyGetter(ExecutableElement builderGetter, String propertyName)283   private void classifyGetter(ExecutableElement builderGetter, String propertyName) {
284     TypeMirror originalGetterType = rewrittenPropertyTypes.get(propertyName);
285     TypeMirror builderGetterType = builderMethodReturnType(builderGetter);
286     String builderGetterTypeString = TypeEncoder.encodeWithAnnotations(builderGetterType);
287     if (TYPE_EQUIVALENCE.equivalent(builderGetterType, originalGetterType)) {
288       builderGetters.put(
289           propertyName,
290           new BuilderSpec.PropertyGetter(builderGetter, builderGetterTypeString, null));
291       return;
292     }
293     Optionalish optional = Optionalish.createIfOptional(builderGetterType);
294     if (optional != null) {
295       TypeMirror containedType = optional.getContainedType(typeUtils);
296       // If the original method is int getFoo() then we allow Optional<Integer> here.
297       // boxedOriginalType is Integer, and containedType is also Integer.
298       // We don't need any special code for OptionalInt because containedType will be int then.
299       TypeMirror boxedOriginalType =
300           originalGetterType.getKind().isPrimitive()
301               ? typeUtils.boxedClass(MoreTypes.asPrimitiveType(originalGetterType)).asType()
302               : null;
303       if (TYPE_EQUIVALENCE.equivalent(containedType, originalGetterType)
304           || TYPE_EQUIVALENCE.equivalent(containedType, boxedOriginalType)) {
305         builderGetters.put(
306             propertyName,
307             new BuilderSpec.PropertyGetter(builderGetter, builderGetterTypeString, optional));
308         return;
309       }
310     }
311     errorReporter.reportError(
312         builderGetter,
313         "[AutoValueBuilderReturnType] Method matches a property of %1$s but has return type %2$s"
314             + " instead of %3$s or an Optional wrapping of %3$s",
315         builtType,
316         builderGetterType,
317         originalGetterType);
318   }
319 
320   /**
321    * Classifies a method given that it has one argument. A method with one argument can be:
322    *
323    * <ul>
324    *   <li>a setter, meaning that it looks like {@code foo(T)} or {@code setFoo(T)}, where the
325    *       {@code AutoValue} class has a property called {@code foo} of type {@code T};
326    *   <li>a property builder with one argument, meaning it looks like {@code
327    *       ImmutableSortedSet.Builder<V> foosBuilder(Comparator<V>)}, where the {@code AutoValue}
328    *       class has a property called {@code foos} with a type whose builder can be made with an
329    *       argument of the given type.
330    * </ul>
331    */
classifyMethodOneArg(ExecutableElement method)332   private void classifyMethodOneArg(ExecutableElement method) {
333     if (classifyPropertyBuilderOneArg(method)) {
334       return;
335     }
336     String methodName = method.getSimpleName().toString();
337     ImmutableMap<String, E> propertyElements = propertyElements();
338     String propertyName = null;
339     E propertyElement = propertyElements.get(methodName);
340     Multimap<String, PropertySetter> propertyNameToSetters = null;
341     if (propertyElement != null) {
342       propertyNameToSetters = propertyNameToUnprefixedSetters;
343       propertyName = methodName;
344     } else if (methodName.startsWith("set") && methodName.length() > 3) {
345       propertyNameToSetters = propertyNameToPrefixedSetters;
346       propertyName = PropertyNames.decapitalizeLikeJavaBeans(methodName.substring(3));
347       propertyElement = propertyElements.get(propertyName);
348       if (propertyElement == null) {
349         // If our property is defined by a getter called getOAuth() then it is called "OAuth"
350         // because of JavaBeans rules. Therefore we want JavaBeans rules to be used for the setter
351         // too, so that you can write setOAuth(x). Meanwhile if the property is defined by a getter
352         // called oAuth() then it is called "oAuth", but you would still expect to be able to set it
353         // using setOAuth(x). Hence the second try using a decapitalize method without the quirky
354         // two-leading-capitals rule.
355         propertyName = PropertyNames.decapitalizeNormally(methodName.substring(3));
356         propertyElement = propertyElements.get(propertyName);
357       }
358     } else {
359       // We might also have an unprefixed setter, so the getter is called OAuth() or getOAuth() and
360       // the setter is called oAuth(x), where again JavaBeans rules imply that it should be called
361       // OAuth(x). Iterating over the properties here is a bit clunky but this case should be
362       // unusual.
363       propertyNameToSetters = propertyNameToUnprefixedSetters;
364       for (Map.Entry<String, E> entry : propertyElements.entrySet()) {
365         if (methodName.equals(PropertyNames.decapitalizeNormally(entry.getKey()))) {
366           propertyName = entry.getKey();
367           propertyElement = entry.getValue();
368           break;
369         }
370       }
371     }
372     if (propertyElement == null || propertyNameToSetters == null) {
373       // The second disjunct isn't needed but convinces control-flow checkers that
374       // propertyNameToSetters can't be null when we call put on it below.
375       errorReporter.reportError(
376           method,
377           "[%sBuilderWhatProp] Method %s does not correspond to %s",
378           autoWhat(),
379           methodName,
380           getterMustMatch());
381       checkForFailedJavaBean(method);
382       return;
383     }
384     Optional<Copier> function = getSetterFunction(propertyElement, method);
385     if (function.isPresent()) {
386       DeclaredType builderTypeMirror = MoreTypes.asDeclared(builderType.asType());
387       ExecutableType methodMirror =
388           MoreTypes.asExecutable(typeUtils.asMemberOf(builderTypeMirror, method));
389       TypeMirror returnType = methodMirror.getReturnType();
390       if (typeUtils.isSubtype(builderType.asType(), returnType)
391           && !MoreTypes.isTypeOf(Object.class, returnType)) {
392         // We allow the return type to be a supertype (other than Object), to support step builders.
393         TypeMirror parameterType = Iterables.getOnlyElement(methodMirror.getParameterTypes());
394         propertyNameToSetters.put(
395             propertyName, new PropertySetter(method, parameterType, function.get()));
396       } else {
397         errorReporter.reportError(
398             method,
399             "[%sBuilderRet] Setter methods must return %s or a supertype",
400             autoWhat(),
401             builderType.asType());
402       }
403     }
404   }
405 
406   /**
407    * Classifies a method given that it has one argument and is a property builder with a parameter,
408    * like {@code ImmutableSortedSet.Builder<String> foosBuilder(Comparator<String>)}.
409    *
410    * @param method A method to classify
411    * @return true if method has been classified successfully
412    */
classifyPropertyBuilderOneArg(ExecutableElement method)413   private boolean classifyPropertyBuilderOneArg(ExecutableElement method) {
414     String methodName = method.getSimpleName().toString();
415     if (!methodName.endsWith("Builder")) {
416       return false;
417     }
418     String property = methodName.substring(0, methodName.length() - "Builder".length());
419     if (!rewrittenPropertyTypes.containsKey(property)) {
420       return false;
421     }
422     PropertyBuilderClassifier propertyBuilderClassifier =
423         new PropertyBuilderClassifier(
424             errorReporter,
425             typeUtils,
426             elementUtils,
427             this,
428             this::propertyIsNullable,
429             rewrittenPropertyTypes,
430             eclipseHack);
431     Optional<PropertyBuilder> maybePropertyBuilder =
432         propertyBuilderClassifier.makePropertyBuilder(method, property);
433     maybePropertyBuilder.ifPresent(
434         propertyBuilder -> propertyNameToPropertyBuilder.put(property, propertyBuilder));
435     return maybePropertyBuilder.isPresent();
436   }
437 
438   /**
439    * Returns an {@code Optional} describing how to convert a value from the setter's parameter type
440    * to the getter's return type, or {@code Optional.empty()} if the conversion isn't possible. An
441    * error will have been reported in the latter case. We can convert if they are already the same
442    * type, when the returned function will be the identity; or if the setter type can be copied
443    * using a method like {@code ImmutableList.copyOf} or {@code Optional.of}, when the returned
444    * function will be something like {@code s -> "Optional.of(" + s + ")"}.
445    */
getSetterFunction(E propertyElement, ExecutableElement setter)446   private Optional<Copier> getSetterFunction(E propertyElement, ExecutableElement setter) {
447     VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters());
448     boolean nullableParameter =
449         nullableAnnotationFor(parameterElement, parameterElement.asType()).isPresent();
450     String property = propertyElements().inverse().get(propertyElement);
451     TypeMirror targetType = rewrittenPropertyTypes.get(property);
452     ExecutableType finalSetter =
453         MoreTypes.asExecutable(
454             typeUtils.asMemberOf(MoreTypes.asDeclared(builderType.asType()), setter));
455     TypeMirror parameterType = finalSetter.getParameterTypes().get(0);
456     // Two types are assignable to each other if they are the same type, or if one is primitive and
457     // the other is the corresponding boxed type. There might be other cases where this is true, but
458     // we're likely to want to accept those too.
459     if (typeUtils.isAssignable(parameterType, targetType)
460         && typeUtils.isAssignable(targetType, parameterType)) {
461       if (nullableParameter) {
462         boolean nullableProperty =
463             nullableAnnotationFor(propertyElement, originalPropertyType(propertyElement))
464                 .isPresent();
465         if (!nullableProperty) {
466           errorReporter.reportError(
467               setter,
468               "[%sNullNotNull] Parameter of setter method is @Nullable but %s is not",
469               autoWhat(),
470               propertyString(propertyElement));
471           return Optional.empty();
472         }
473       }
474       return Optional.of(Copier.IDENTITY);
475     }
476 
477     // Parameter type is not equal to property type, but might be convertible with copyOf.
478     ImmutableList<ExecutableElement> copyOfMethods = copyOfMethods(targetType, nullableParameter);
479     if (!copyOfMethods.isEmpty()) {
480       return getConvertingSetterFunction(copyOfMethods, propertyElement, setter, parameterType);
481     }
482     errorReporter.reportError(
483         setter,
484         "[%sGetVsSet] Parameter type %s of setter method should be %s to match %s",
485         autoWhat(),
486         parameterType,
487         targetType,
488         propertyString(propertyElement));
489     return Optional.empty();
490   }
491 
492   /**
493    * Returns an {@code Optional} describing how to convert a value from the setter's parameter type
494    * to the getter's return type using one of the given methods, or {@code Optional.empty()} if the
495    * conversion isn't possible. An error will have been reported in the latter case.
496    */
getConvertingSetterFunction( ImmutableList<ExecutableElement> copyOfMethods, E propertyElement, ExecutableElement setter, TypeMirror parameterType)497   private Optional<Copier> getConvertingSetterFunction(
498       ImmutableList<ExecutableElement> copyOfMethods,
499       E propertyElement,
500       ExecutableElement setter,
501       TypeMirror parameterType) {
502     String property = propertyElements().inverse().get(propertyElement);
503     DeclaredType targetType = MoreTypes.asDeclared(rewrittenPropertyTypes.get(property));
504     for (ExecutableElement copyOfMethod : copyOfMethods) {
505       Optional<Copier> function =
506           getConvertingSetterFunction(copyOfMethod, targetType, parameterType);
507       if (function.isPresent()) {
508         return function;
509       }
510     }
511     String targetTypeSimpleName = targetType.asElement().getSimpleName().toString();
512     errorReporter.reportError(
513         setter,
514         "[%sGetVsSetOrConvert] Parameter type %s of setter method should be %s to match %s, or it"
515             + " should be a type that can be passed to %s.%s to produce %s",
516         autoWhat(),
517         parameterType,
518         targetType,
519         propertyString(propertyElement),
520         targetTypeSimpleName,
521         copyOfMethods.get(0).getSimpleName(),
522         targetType);
523     return Optional.empty();
524   }
525 
526   /**
527    * Returns an {@code Optional} containing a function to use {@code copyOfMethod} to copy the
528    * {@code parameterType} to the {@code targetType}, or {@code Optional.empty()} if the method
529    * can't be used. For example, we might have a property of type {@code ImmutableSet<T>} and our
530    * setter has a parameter of type {@code Set<? extends T>}. Can we use {@code ImmutableSet<E>
531    * ImmutableSet.copyOf(Collection<? extends E>)} to set the property? What about {@code
532    * ImmutableSet<E> ImmutableSet.copyOf(E[])}?
533    *
534    * <p>The example here is deliberately complicated, in that it has a type parameter of its own,
535    * presumably because the {@code @AutoValue} class is {@code Foo<T>}. One subtle point is that the
536    * builder will then be {@code Builder<T>} where this {@code T} is a <i>different</i> type
537    * variable. However, we've used {@link TypeVariables} to ensure that the {@code T} in {@code
538    * ImmutableSet<T>} is actually the one from {@code Builder<T>} instead of the original one from
539    * {@code Foo<T>}.}
540    *
541    * @param copyOfMethod the candidate method to do the copy, {@code
542    *     ImmutableSet.copyOf(Collection<? extends E>)} or {@code ImmutableSet.copyOf(E[])} in the
543    *     examples.
544    * @param targetType the type of the property to be set, {@code ImmutableSet<T>} in the example.
545    * @param parameterType the type of the setter parameter, {@code Set<? extends T>} in the example.
546    * @return a function that maps a string parameter to a method call using that parameter. For
547    *     example it might map {@code foo} to {@code ImmutableList.copyOf(foo)}.
548    */
getConvertingSetterFunction( ExecutableElement copyOfMethod, DeclaredType targetType, TypeMirror parameterType)549   private Optional<Copier> getConvertingSetterFunction(
550       ExecutableElement copyOfMethod, DeclaredType targetType, TypeMirror parameterType) {
551     // We have a parameter type, for example Set<? extends T>, and we want to know if it can be
552     // passed to the given copyOf method, which might for example be one of these methods from
553     // ImmutableSet:
554     //    public static <E> ImmutableSet<E> copyOf(Collection<? extends E> elements)
555     //    public static <E> ImmutableSet<E> copyOf(E[] elements)
556     // Additionally, if it can indeed be passed to the method, we want to know whether the result
557     // (here ImmutableSet<? extends T>) is compatible with the property to be set.
558     // We can't use Types.asMemberOf to do the substitution for us, because the methods in question
559     // are static. So even if our target type is ImmutableSet<String>, if we ask what the type of
560     // copyOf is in ImmutableSet<String> it will still tell us <T> Optional<T> (T).
561     // Instead, we do the variable substitutions ourselves.
562     if (TypeVariables.canAssignStaticMethodResult(
563         copyOfMethod, parameterType, targetType, typeUtils)) {
564       String method = TypeEncoder.encodeRaw(targetType) + "." + copyOfMethod.getSimpleName();
565       Function<String, String> callMethod = s -> method + "(" + s + ")";
566       // This is a big old hack. We guess that the method can accept a null parameter if it has
567       // "Nullable" in the name, which java.util.Optional.ofNullable and
568       // com.google.common.base.Optional.fromNullable do.
569       Copier copier =
570           method.contains("Nullable")
571               ? Copier.acceptingNull(callMethod)
572               : Copier.notAcceptingNull(callMethod);
573       return Optional.of(copier);
574     }
575     return Optional.empty();
576   }
577 
578   /**
579    * Returns {@code copyOf} methods from the given type. These are static methods with a single
580    * parameter, called {@code copyOf} or {@code copyOfSorted} for Guava collection types, and called
581    * {@code of} or {@code ofNullable} for {@code Optional}. All of Guava's concrete immutable
582    * collection types have at least one such method, but we will also accept other classes with an
583    * appropriate {@code copyOf} method, such as {@link java.util.EnumSet}.
584    */
copyOfMethods( TypeMirror targetType, boolean nullableParameter)585   private ImmutableList<ExecutableElement> copyOfMethods(
586       TypeMirror targetType, boolean nullableParameter) {
587     if (!targetType.getKind().equals(TypeKind.DECLARED)) {
588       return ImmutableList.of();
589     }
590     ImmutableSet<String> copyOfNames;
591     Optionalish optionalish = Optionalish.createIfOptional(targetType);
592     if (optionalish == null) {
593       copyOfNames = ImmutableSet.of("copyOfSorted", "copyOf");
594     } else {
595       copyOfNames = ImmutableSet.of(nullableParameter ? optionalish.ofNullable() : "of");
596     }
597     TypeElement targetTypeElement = MoreElements.asType(typeUtils.asElement(targetType));
598     ImmutableList.Builder<ExecutableElement> copyOfMethods = ImmutableList.builder();
599     for (String copyOfName : copyOfNames) {
600       for (ExecutableElement method :
601           ElementFilter.methodsIn(targetTypeElement.getEnclosedElements())) {
602         if (method.getSimpleName().contentEquals(copyOfName)
603             && method.getParameters().size() == 1
604             && method.getModifiers().contains(Modifier.STATIC)) {
605           copyOfMethods.add(method);
606         }
607       }
608     }
609     return copyOfMethods.build();
610   }
611 
612   /**
613    * Returns the return type of the given method from the builder. This should be the final type of
614    * the method when any bound type variables are substituted. Consider this example:
615    *
616    * <pre>{@code
617    * abstract static class ParentBuilder<B extends ParentBuilder> {
618    *   B setFoo(String s);
619    * }
620    * abstract static class ChildBuilder extends ParentBuilder<ChildBuilder> {
621    *   ...
622    * }
623    * }</pre>
624    *
625    * If the builder is {@code ChildBuilder} then the return type of {@code setFoo} is also {@code
626    * ChildBuilder}, and not {@code B} as its {@code getReturnType()} method would claim.
627    *
628    * <p>If the caller is in a version of Eclipse with <a
629    * href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=382590">this bug</a> then the {@code
630    * asMemberOf} call will fail if the method is inherited from an interface. We work around that
631    * for methods in the {@code @AutoValue} class using {@link EclipseHack#methodReturnTypes} but we
632    * don't try to do so here because it should be much less likely. You might need to change {@code
633    * ParentBuilder} from an interface to an abstract class to make it work, but you'll often need to
634    * do that anyway.
635    */
builderMethodReturnType(ExecutableElement builderMethod)636   TypeMirror builderMethodReturnType(ExecutableElement builderMethod) {
637     DeclaredType builderTypeMirror = MoreTypes.asDeclared(builderType.asType());
638     TypeMirror methodMirror;
639     try {
640       methodMirror = typeUtils.asMemberOf(builderTypeMirror, builderMethod);
641     } catch (IllegalArgumentException e) {
642       // Presumably we've hit the Eclipse bug cited.
643       return builderMethod.getReturnType();
644     }
645     return MoreTypes.asExecutable(methodMirror).getReturnType();
646   }
647 
prefixWithSet(String propertyName)648   private static String prefixWithSet(String propertyName) {
649     // This is not internationalizationally correct, but it corresponds to what
650     // Introspector.decapitalize does.
651     return "set" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
652   }
653 
654   /**
655    * True if the given property is nullable, either because its type has a {@code @Nullable} type
656    * annotation, or because its getter method has a {@code @Nullable} method annotation.
657    */
propertyIsNullable(String property)658   private boolean propertyIsNullable(String property) {
659     E propertyElement = propertyElements().get(property);
660     return Stream.of(propertyElement, originalPropertyType(propertyElement))
661         .flatMap(ac -> ac.getAnnotationMirrors().stream())
662         .map(a -> a.getAnnotationType().asElement().getSimpleName())
663         .anyMatch(n -> n.contentEquals("Nullable"));
664   }
665 
666   /**
667    * Returns a map from property names to the corresponding source program elements. For AutoValue,
668    * these elements are the abstract getter methods in the {@code @AutoValue} class. For
669    * AutoBuilder, they are the parameters of the constructor or method that the generated builder
670    * will call.
671    */
propertyElements()672   abstract ImmutableBiMap<String, E> propertyElements();
673 
674   /**
675    * Returns the property type as it appears on the original source program element. This can be
676    * different from the type stored in {@link #rewrittenPropertyTypes} since that one will refer to
677    * type variables in the builder rather than in the original class. Also, {@link
678    * #rewrittenPropertyTypes} will not have type annotations even if they were present on the
679    * original element, so {@code originalPropertyType} is the right thing to use for those.
680    */
originalPropertyType(E propertyElement)681   abstract TypeMirror originalPropertyType(E propertyElement);
682 
683   /**
684    * A string identifying the given property element, which is a method for AutoValue or a parameter
685    * for AutoBuilder.
686    */
propertyString(E propertyElement)687   abstract String propertyString(E propertyElement);
688 
689   /**
690    * Returns the name of the property that the given no-arg builder method queries, if
691    * any. For example, if your {@code @AutoValue} class has a method {@code abstract String
692    * getBar()} then an abstract method in its builder with the same signature will query the {@code
693    * bar} property.
694    */
propertyForBuilderGetter(ExecutableElement method)695   abstract Optional<String> propertyForBuilderGetter(ExecutableElement method);
696 
697   /**
698    * Checks for failed JavaBean usage when a method that looks like a setter doesn't actually match
699    * anything, and emits a compiler Note if detected. A frequent source of problems is where the
700    * JavaBeans conventions have been followed for most but not all getters. Then AutoValue considers
701    * that they haven't been followed at all, so you might have a property called getFoo where you
702    * thought it was called just foo, and you might not understand why your setter called setFoo is
703    * rejected (it would have to be called setGetFoo).
704    *
705    * <p>This is not relevant for AutoBuilder, which uses parameter names rather than getters. The
706    * parameter names are unambiguously the same as the property names.
707    */
checkForFailedJavaBean(ExecutableElement rejectedSetter)708   abstract void checkForFailedJavaBean(ExecutableElement rejectedSetter);
709 
710   /**
711    * A string describing what sort of Auto this is, {@code "AutoValue"} or {@code "AutoBuilder"}.
712    */
autoWhat()713   abstract String autoWhat();
714 
715   /**
716    * A string describing what a builder getter must match: a property method for AutoValue, a
717    * parameter for AutoBuilder.
718    */
getterMustMatch()719   abstract String getterMustMatch();
720 
721   /**
722    * A string describing what a property builder for property {@code foo} must match, {@code foo()
723    * or getFoo()} for AutoValue, {@code foo} for AutoBuilder.
724    */
fooBuilderMustMatch()725   abstract String fooBuilderMustMatch();
726 }
727