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 if (TYPE_EQUIVALENCE.equivalent(methodMirror.getReturnType(), builderType.asType())) { 390 TypeMirror parameterType = Iterables.getOnlyElement(methodMirror.getParameterTypes()); 391 propertyNameToSetters.put( 392 propertyName, new PropertySetter(method, parameterType, function.get())); 393 } else { 394 errorReporter.reportError( 395 method, 396 "[%sBuilderRet] Setter methods must return %s", 397 autoWhat(), 398 builderType.asType()); 399 } 400 } 401 } 402 403 /** 404 * Classifies a method given that it has one argument and is a property builder with a parameter, 405 * like {@code ImmutableSortedSet.Builder<String> foosBuilder(Comparator<String>)}. 406 * 407 * @param method A method to classify 408 * @return true if method has been classified successfully 409 */ classifyPropertyBuilderOneArg(ExecutableElement method)410 private boolean classifyPropertyBuilderOneArg(ExecutableElement method) { 411 String methodName = method.getSimpleName().toString(); 412 if (!methodName.endsWith("Builder")) { 413 return false; 414 } 415 String property = methodName.substring(0, methodName.length() - "Builder".length()); 416 if (!rewrittenPropertyTypes.containsKey(property)) { 417 return false; 418 } 419 PropertyBuilderClassifier propertyBuilderClassifier = 420 new PropertyBuilderClassifier( 421 errorReporter, 422 typeUtils, 423 elementUtils, 424 this, 425 this::propertyIsNullable, 426 rewrittenPropertyTypes, 427 eclipseHack); 428 Optional<PropertyBuilder> maybePropertyBuilder = 429 propertyBuilderClassifier.makePropertyBuilder(method, property); 430 maybePropertyBuilder.ifPresent( 431 propertyBuilder -> propertyNameToPropertyBuilder.put(property, propertyBuilder)); 432 return maybePropertyBuilder.isPresent(); 433 } 434 435 /** 436 * Returns an {@code Optional} describing how to convert a value from the setter's parameter type 437 * to the getter's return type, or {@code Optional.empty()} if the conversion isn't possible. An 438 * error will have been reported in the latter case. We can convert if they are already the same 439 * type, when the returned function will be the identity; or if the setter type can be copied 440 * using a method like {@code ImmutableList.copyOf} or {@code Optional.of}, when the returned 441 * function will be something like {@code s -> "Optional.of(" + s + ")"}. 442 */ getSetterFunction(E propertyElement, ExecutableElement setter)443 private Optional<Copier> getSetterFunction(E propertyElement, ExecutableElement setter) { 444 VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters()); 445 boolean nullableParameter = 446 nullableAnnotationFor(parameterElement, parameterElement.asType()).isPresent(); 447 String property = propertyElements().inverse().get(propertyElement); 448 TypeMirror targetType = rewrittenPropertyTypes.get(property); 449 ExecutableType finalSetter = 450 MoreTypes.asExecutable( 451 typeUtils.asMemberOf(MoreTypes.asDeclared(builderType.asType()), setter)); 452 TypeMirror parameterType = finalSetter.getParameterTypes().get(0); 453 // Two types are assignable to each other if they are the same type, or if one is primitive and 454 // the other is the corresponding boxed type. There might be other cases where this is true, but 455 // we're likely to want to accept those too. 456 if (typeUtils.isAssignable(parameterType, targetType) 457 && typeUtils.isAssignable(targetType, parameterType)) { 458 if (nullableParameter) { 459 boolean nullableProperty = 460 nullableAnnotationFor(propertyElement, originalPropertyType(propertyElement)) 461 .isPresent(); 462 if (!nullableProperty) { 463 errorReporter.reportError( 464 setter, 465 "[%sNullNotNull] Parameter of setter method is @Nullable but %s is not", 466 autoWhat(), 467 propertyString(propertyElement)); 468 return Optional.empty(); 469 } 470 } 471 return Optional.of(Copier.IDENTITY); 472 } 473 474 // Parameter type is not equal to property type, but might be convertible with copyOf. 475 ImmutableList<ExecutableElement> copyOfMethods = copyOfMethods(targetType, nullableParameter); 476 if (!copyOfMethods.isEmpty()) { 477 return getConvertingSetterFunction(copyOfMethods, propertyElement, setter, parameterType); 478 } 479 errorReporter.reportError( 480 setter, 481 "[%sGetVsSet] Parameter type %s of setter method should be %s to match %s", 482 autoWhat(), 483 parameterType, 484 targetType, 485 propertyString(propertyElement)); 486 return Optional.empty(); 487 } 488 489 /** 490 * Returns an {@code Optional} describing how to convert a value from the setter's parameter type 491 * to the getter's return type using one of the given methods, or {@code Optional.empty()} if the 492 * conversion isn't possible. An error will have been reported in the latter case. 493 */ getConvertingSetterFunction( ImmutableList<ExecutableElement> copyOfMethods, E propertyElement, ExecutableElement setter, TypeMirror parameterType)494 private Optional<Copier> getConvertingSetterFunction( 495 ImmutableList<ExecutableElement> copyOfMethods, 496 E propertyElement, 497 ExecutableElement setter, 498 TypeMirror parameterType) { 499 String property = propertyElements().inverse().get(propertyElement); 500 DeclaredType targetType = MoreTypes.asDeclared(rewrittenPropertyTypes.get(property)); 501 for (ExecutableElement copyOfMethod : copyOfMethods) { 502 Optional<Copier> function = 503 getConvertingSetterFunction(copyOfMethod, targetType, parameterType); 504 if (function.isPresent()) { 505 return function; 506 } 507 } 508 String targetTypeSimpleName = targetType.asElement().getSimpleName().toString(); 509 errorReporter.reportError( 510 setter, 511 "[%sGetVsSetOrConvert] Parameter type %s of setter method should be %s to match %s, or it" 512 + " should be a type that can be passed to %s.%s to produce %s", 513 autoWhat(), 514 parameterType, 515 targetType, 516 propertyString(propertyElement), 517 targetTypeSimpleName, 518 copyOfMethods.get(0).getSimpleName(), 519 targetType); 520 return Optional.empty(); 521 } 522 523 /** 524 * Returns an {@code Optional} containing a function to use {@code copyOfMethod} to copy the 525 * {@code parameterType} to the {@code targetType}, or {@code Optional.empty()} if the method 526 * can't be used. For example, we might have a property of type {@code ImmutableSet<T>} and our 527 * setter has a parameter of type {@code Set<? extends T>}. Can we use {@code ImmutableSet<E> 528 * ImmutableSet.copyOf(Collection<? extends E>)} to set the property? What about {@code 529 * ImmutableSet<E> ImmutableSet.copyOf(E[])}? 530 * 531 * <p>The example here is deliberately complicated, in that it has a type parameter of its own, 532 * presumably because the {@code @AutoValue} class is {@code Foo<T>}. One subtle point is that the 533 * builder will then be {@code Builder<T>} where this {@code T} is a <i>different</i> type 534 * variable. However, we've used {@link TypeVariables} to ensure that the {@code T} in {@code 535 * ImmutableSet<T>} is actually the one from {@code Builder<T>} instead of the original one from 536 * {@code Foo<T>}.} 537 * 538 * @param copyOfMethod the candidate method to do the copy, {@code 539 * ImmutableSet.copyOf(Collection<? extends E>)} or {@code ImmutableSet.copyOf(E[])} in the 540 * examples. 541 * @param targetType the type of the property to be set, {@code ImmutableSet<T>} in the example. 542 * @param parameterType the type of the setter parameter, {@code Set<? extends T>} in the example. 543 * @return a function that maps a string parameter to a method call using that parameter. For 544 * example it might map {@code foo} to {@code ImmutableList.copyOf(foo)}. 545 */ getConvertingSetterFunction( ExecutableElement copyOfMethod, DeclaredType targetType, TypeMirror parameterType)546 private Optional<Copier> getConvertingSetterFunction( 547 ExecutableElement copyOfMethod, DeclaredType targetType, TypeMirror parameterType) { 548 // We have a parameter type, for example Set<? extends T>, and we want to know if it can be 549 // passed to the given copyOf method, which might for example be one of these methods from 550 // ImmutableSet: 551 // public static <E> ImmutableSet<E> copyOf(Collection<? extends E> elements) 552 // public static <E> ImmutableSet<E> copyOf(E[] elements) 553 // Additionally, if it can indeed be passed to the method, we want to know whether the result 554 // (here ImmutableSet<? extends T>) is compatible with the property to be set. 555 // We can't use Types.asMemberOf to do the substitution for us, because the methods in question 556 // are static. So even if our target type is ImmutableSet<String>, if we ask what the type of 557 // copyOf is in ImmutableSet<String> it will still tell us <T> Optional<T> (T). 558 // Instead, we do the variable substitutions ourselves. 559 if (TypeVariables.canAssignStaticMethodResult( 560 copyOfMethod, parameterType, targetType, typeUtils)) { 561 String method = TypeEncoder.encodeRaw(targetType) + "." + copyOfMethod.getSimpleName(); 562 Function<String, String> callMethod = s -> method + "(" + s + ")"; 563 // This is a big old hack. We guess that the method can accept a null parameter if it has 564 // "Nullable" in the name, which java.util.Optional.ofNullable and 565 // com.google.common.base.Optional.fromNullable do. 566 Copier copier = 567 method.contains("Nullable") 568 ? Copier.acceptingNull(callMethod) 569 : Copier.notAcceptingNull(callMethod); 570 return Optional.of(copier); 571 } 572 return Optional.empty(); 573 } 574 575 /** 576 * Returns {@code copyOf} methods from the given type. These are static methods with a single 577 * parameter, called {@code copyOf} or {@code copyOfSorted} for Guava collection types, and called 578 * {@code of} or {@code ofNullable} for {@code Optional}. All of Guava's concrete immutable 579 * collection types have at least one such method, but we will also accept other classes with an 580 * appropriate {@code copyOf} method, such as {@link java.util.EnumSet}. 581 */ copyOfMethods( TypeMirror targetType, boolean nullableParameter)582 private ImmutableList<ExecutableElement> copyOfMethods( 583 TypeMirror targetType, boolean nullableParameter) { 584 if (!targetType.getKind().equals(TypeKind.DECLARED)) { 585 return ImmutableList.of(); 586 } 587 ImmutableSet<String> copyOfNames; 588 Optionalish optionalish = Optionalish.createIfOptional(targetType); 589 if (optionalish == null) { 590 copyOfNames = ImmutableSet.of("copyOfSorted", "copyOf"); 591 } else { 592 copyOfNames = ImmutableSet.of(nullableParameter ? optionalish.ofNullable() : "of"); 593 } 594 TypeElement targetTypeElement = MoreElements.asType(typeUtils.asElement(targetType)); 595 ImmutableList.Builder<ExecutableElement> copyOfMethods = ImmutableList.builder(); 596 for (String copyOfName : copyOfNames) { 597 for (ExecutableElement method : 598 ElementFilter.methodsIn(targetTypeElement.getEnclosedElements())) { 599 if (method.getSimpleName().contentEquals(copyOfName) 600 && method.getParameters().size() == 1 601 && method.getModifiers().contains(Modifier.STATIC)) { 602 copyOfMethods.add(method); 603 } 604 } 605 } 606 return copyOfMethods.build(); 607 } 608 609 /** 610 * Returns the return type of the given method from the builder. This should be the final type of 611 * the method when any bound type variables are substituted. Consider this example: 612 * 613 * <pre>{@code 614 * abstract static class ParentBuilder<B extends ParentBuilder> { 615 * B setFoo(String s); 616 * } 617 * abstract static class ChildBuilder extends ParentBuilder<ChildBuilder> { 618 * ... 619 * } 620 * }</pre> 621 * 622 * If the builder is {@code ChildBuilder} then the return type of {@code setFoo} is also {@code 623 * ChildBuilder}, and not {@code B} as its {@code getReturnType()} method would claim. 624 * 625 * <p>If the caller is in a version of Eclipse with <a 626 * href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=382590">this bug</a> then the {@code 627 * asMemberOf} call will fail if the method is inherited from an interface. We work around that 628 * for methods in the {@code @AutoValue} class using {@link EclipseHack#methodReturnTypes} but we 629 * don't try to do so here because it should be much less likely. You might need to change {@code 630 * ParentBuilder} from an interface to an abstract class to make it work, but you'll often need to 631 * do that anyway. 632 */ builderMethodReturnType(ExecutableElement builderMethod)633 TypeMirror builderMethodReturnType(ExecutableElement builderMethod) { 634 DeclaredType builderTypeMirror = MoreTypes.asDeclared(builderType.asType()); 635 TypeMirror methodMirror; 636 try { 637 methodMirror = typeUtils.asMemberOf(builderTypeMirror, builderMethod); 638 } catch (IllegalArgumentException e) { 639 // Presumably we've hit the Eclipse bug cited. 640 return builderMethod.getReturnType(); 641 } 642 return MoreTypes.asExecutable(methodMirror).getReturnType(); 643 } 644 prefixWithSet(String propertyName)645 private static String prefixWithSet(String propertyName) { 646 // This is not internationalizationally correct, but it corresponds to what 647 // Introspector.decapitalize does. 648 return "set" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1); 649 } 650 651 /** 652 * True if the given property is nullable, either because its type has a {@code @Nullable} type 653 * annotation, or because its getter method has a {@code @Nullable} method annotation. 654 */ propertyIsNullable(String property)655 private boolean propertyIsNullable(String property) { 656 E propertyElement = propertyElements().get(property); 657 return Stream.of(propertyElement, originalPropertyType(propertyElement)) 658 .flatMap(ac -> ac.getAnnotationMirrors().stream()) 659 .map(a -> a.getAnnotationType().asElement().getSimpleName()) 660 .anyMatch(n -> n.contentEquals("Nullable")); 661 } 662 663 /** 664 * Returns a map from property names to the corresponding source program elements. For AutoValue, 665 * these elements are the abstract getter methods in the {@code @AutoValue} class. For 666 * AutoBuilder, they are the parameters of the constructor or method that the generated builder 667 * will call. 668 */ propertyElements()669 abstract ImmutableBiMap<String, E> propertyElements(); 670 671 /** 672 * Returns the property type as it appears on the original source program element. This can be 673 * different from the type stored in {@link #rewrittenPropertyTypes} since that one will refer to 674 * type variables in the builder rather than in the original class. Also, {@link 675 * #rewrittenPropertyTypes} will not have type annotations even if they were present on the 676 * original element, so {@code originalPropertyType} is the right thing to use for those. 677 */ originalPropertyType(E propertyElement)678 abstract TypeMirror originalPropertyType(E propertyElement); 679 680 /** 681 * A string identifying the given property element, which is a method for AutoValue or a parameter 682 * for AutoBuilder. 683 */ propertyString(E propertyElement)684 abstract String propertyString(E propertyElement); 685 686 /** 687 * Returns the name of the property that the given no-arg builder method queries, if 688 * any. For example, if your {@code @AutoValue} class has a method {@code abstract String 689 * getBar()} then an abstract method in its builder with the same signature will query the {@code 690 * bar} property. 691 */ propertyForBuilderGetter(ExecutableElement method)692 abstract Optional<String> propertyForBuilderGetter(ExecutableElement method); 693 694 /** 695 * Checks for failed JavaBean usage when a method that looks like a setter doesn't actually match 696 * anything, and emits a compiler Note if detected. A frequent source of problems is where the 697 * JavaBeans conventions have been followed for most but not all getters. Then AutoValue considers 698 * that they haven't been followed at all, so you might have a property called getFoo where you 699 * thought it was called just foo, and you might not understand why your setter called setFoo is 700 * rejected (it would have to be called setGetFoo). 701 * 702 * <p>This is not relevant for AutoBuilder, which uses parameter names rather than getters. The 703 * parameter names are unambiguously the same as the property names. 704 */ checkForFailedJavaBean(ExecutableElement rejectedSetter)705 abstract void checkForFailedJavaBean(ExecutableElement rejectedSetter); 706 707 /** 708 * A string describing what sort of Auto this is, {@code "AutoValue"} or {@code "AutoBuilder"}. 709 */ autoWhat()710 abstract String autoWhat(); 711 712 /** 713 * A string describing what a builder getter must match: a property method for AutoValue, a 714 * parameter for AutoBuilder. 715 */ getterMustMatch()716 abstract String getterMustMatch(); 717 718 /** 719 * A string describing what a property builder for property {@code foo} must match, {@code foo() 720 * or getFoo()} for AutoValue, {@code foo} for AutoBuilder. 721 */ fooBuilderMustMatch()722 abstract String fooBuilderMustMatch(); 723 } 724