1 /* 2 * Copyright 2014 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.common.MoreElements.getLocalAndInheritedMethods; 19 import static com.google.auto.value.processor.AutoValueOrOneOfProcessor.hasAnnotationMirror; 20 import static com.google.auto.value.processor.AutoValueOrOneOfProcessor.nullableAnnotationFor; 21 import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_BUILDER_NAME; 22 import static com.google.common.collect.Sets.immutableEnumSet; 23 import static java.util.stream.Collectors.toList; 24 import static java.util.stream.Collectors.toSet; 25 import static javax.lang.model.util.ElementFilter.methodsIn; 26 import static javax.lang.model.util.ElementFilter.typesIn; 27 28 import com.google.auto.common.MoreTypes; 29 import com.google.auto.value.extension.AutoValueExtension; 30 import com.google.auto.value.processor.AutoValueOrOneOfProcessor.Property; 31 import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder; 32 import com.google.common.collect.ImmutableBiMap; 33 import com.google.common.collect.ImmutableMap; 34 import com.google.common.collect.ImmutableSet; 35 import com.google.common.collect.Iterables; 36 import com.google.common.collect.Maps; 37 import java.util.LinkedHashSet; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.Optional; 41 import java.util.Set; 42 import java.util.function.Function; 43 import javax.annotation.processing.ProcessingEnvironment; 44 import javax.lang.model.element.Element; 45 import javax.lang.model.element.ElementKind; 46 import javax.lang.model.element.ExecutableElement; 47 import javax.lang.model.element.Modifier; 48 import javax.lang.model.element.TypeElement; 49 import javax.lang.model.element.TypeParameterElement; 50 import javax.lang.model.element.VariableElement; 51 import javax.lang.model.type.DeclaredType; 52 import javax.lang.model.type.TypeKind; 53 import javax.lang.model.type.TypeMirror; 54 import javax.lang.model.util.Types; 55 56 /** 57 * Support for AutoValue builders. 58 * 59 * @author Éamonn McManus 60 */ 61 class BuilderSpec { 62 private final TypeElement autoValueClass; 63 private final ProcessingEnvironment processingEnv; 64 private final ErrorReporter errorReporter; 65 BuilderSpec( TypeElement autoValueClass, ProcessingEnvironment processingEnv, ErrorReporter errorReporter)66 BuilderSpec( 67 TypeElement autoValueClass, 68 ProcessingEnvironment processingEnv, 69 ErrorReporter errorReporter) { 70 this.autoValueClass = autoValueClass; 71 this.processingEnv = processingEnv; 72 this.errorReporter = errorReporter; 73 } 74 75 private static final ImmutableSet<ElementKind> CLASS_OR_INTERFACE = 76 immutableEnumSet(ElementKind.CLASS, ElementKind.INTERFACE); 77 78 /** 79 * Determines if the {@code @AutoValue} class for this instance has a correct nested 80 * {@code @AutoValue.Builder} class or interface and return a representation of it in an {@code 81 * Optional} if so. 82 */ getBuilder()83 Optional<Builder> getBuilder() { 84 Optional<TypeElement> builderTypeElement = Optional.empty(); 85 for (TypeElement containedClass : typesIn(autoValueClass.getEnclosedElements())) { 86 if (hasAnnotationMirror(containedClass, AUTO_VALUE_BUILDER_NAME)) { 87 if (!CLASS_OR_INTERFACE.contains(containedClass.getKind())) { 88 errorReporter.reportError( 89 containedClass, 90 "[AutoValueBuilderClass] @AutoValue.Builder can only apply to a class or an" 91 + " interface"); 92 } else if (!containedClass.getModifiers().contains(Modifier.STATIC)) { 93 errorReporter.reportError( 94 containedClass, 95 "[AutoValueInnerBuilder] @AutoValue.Builder cannot be applied to a non-static class"); 96 } else if (builderTypeElement.isPresent()) { 97 errorReporter.reportError( 98 containedClass, 99 "[AutoValueTwoBuilders] %s already has a Builder: %s", 100 autoValueClass, 101 builderTypeElement.get()); 102 } else { 103 builderTypeElement = Optional.of(containedClass); 104 } 105 } 106 } 107 108 if (builderTypeElement.isPresent()) { 109 return builderFrom(builderTypeElement.get()); 110 } else { 111 return Optional.empty(); 112 } 113 } 114 115 /** Representation of an {@code AutoValue.Builder} class or interface. */ 116 class Builder implements AutoValueExtension.BuilderContext { 117 private final TypeElement builderTypeElement; 118 private ImmutableSet<ExecutableElement> toBuilderMethods; 119 private ExecutableElement buildMethod; 120 private BuilderMethodClassifier classifier; 121 Builder(TypeElement builderTypeElement)122 Builder(TypeElement builderTypeElement) { 123 this.builderTypeElement = builderTypeElement; 124 } 125 126 @Override builderType()127 public TypeElement builderType() { 128 return builderTypeElement; 129 } 130 131 @Override builderMethods()132 public Set<ExecutableElement> builderMethods() { 133 return methodsIn(autoValueClass.getEnclosedElements()).stream() 134 .filter( 135 m -> 136 m.getParameters().isEmpty() 137 && m.getModifiers().contains(Modifier.STATIC) 138 && !m.getModifiers().contains(Modifier.PRIVATE) 139 && erasedTypeIs(m.getReturnType(), builderTypeElement)) 140 .collect(toSet()); 141 } 142 143 @Override buildMethod()144 public Optional<ExecutableElement> buildMethod() { 145 return methodsIn(builderTypeElement.getEnclosedElements()).stream() 146 .filter( 147 m -> 148 m.getSimpleName().contentEquals("build") 149 && !m.getModifiers().contains(Modifier.PRIVATE) 150 && !m.getModifiers().contains(Modifier.STATIC) 151 && m.getParameters().isEmpty() 152 && erasedTypeIs(m.getReturnType(), autoValueClass)) 153 .findFirst(); 154 } 155 156 @Override autoBuildMethod()157 public ExecutableElement autoBuildMethod() { 158 return buildMethod; 159 } 160 161 @Override setters()162 public Map<String, Set<ExecutableElement>> setters() { 163 return Maps.transformValues( 164 classifier.propertyNameToSetters().asMap(), 165 propertySetters -> 166 propertySetters.stream().map(PropertySetter::getSetter).collect(toSet())); 167 } 168 169 @Override propertyBuilders()170 public Map<String, ExecutableElement> propertyBuilders() { 171 return Maps.transformValues( 172 classifier.propertyNameToPropertyBuilder(), PropertyBuilder::getPropertyBuilderMethod); 173 } 174 erasedTypeIs(TypeMirror type, TypeElement baseType)175 private boolean erasedTypeIs(TypeMirror type, TypeElement baseType) { 176 return type.getKind().equals(TypeKind.DECLARED) 177 && MoreTypes.asDeclared(type).asElement().equals(baseType); 178 } 179 180 @Override toBuilderMethods()181 public Set<ExecutableElement> toBuilderMethods() { 182 return toBuilderMethods; 183 } 184 185 /** 186 * Finds any methods in the set that return the builder type. If the builder has type parameters 187 * {@code <A, B>}, then the return type of the method must be {@code Builder<A, B>} with the 188 * same parameter names. We enforce elsewhere that the names and bounds of the builder 189 * parameters must be the same as those of the @AutoValue class. Here's a correct example: 190 * 191 * <pre> 192 * {@code @AutoValue abstract class Foo<A extends Number, B> { 193 * abstract int someProperty(); 194 * 195 * abstract Builder<A, B> toBuilder(); 196 * 197 * interface Builder<A extends Number, B> {...} 198 * }} 199 * </pre> 200 * 201 * <p>We currently impose that there cannot be more than one such method. 202 */ toBuilderMethods( Types typeUtils, Set<ExecutableElement> abstractMethods)203 ImmutableSet<ExecutableElement> toBuilderMethods( 204 Types typeUtils, Set<ExecutableElement> abstractMethods) { 205 206 List<String> builderTypeParamNames = 207 builderTypeElement.getTypeParameters().stream() 208 .map(e -> e.getSimpleName().toString()) 209 .collect(toList()); 210 211 ImmutableSet.Builder<ExecutableElement> methods = ImmutableSet.builder(); 212 for (ExecutableElement method : abstractMethods) { 213 if (builderTypeElement.equals(typeUtils.asElement(method.getReturnType()))) { 214 methods.add(method); 215 DeclaredType returnType = MoreTypes.asDeclared(method.getReturnType()); 216 List<String> typeArguments = 217 returnType.getTypeArguments().stream() 218 .filter(t -> t.getKind().equals(TypeKind.TYPEVAR)) 219 .map(t -> typeUtils.asElement(t).getSimpleName().toString()) 220 .collect(toList()); 221 if (!builderTypeParamNames.equals(typeArguments)) { 222 errorReporter.reportError( 223 method, 224 "[AutoValueBuilderConverterReturn] Builder converter method should return %s%s", 225 builderTypeElement, 226 TypeSimplifier.actualTypeParametersString(builderTypeElement)); 227 } 228 } 229 } 230 ImmutableSet<ExecutableElement> builderMethods = methods.build(); 231 if (builderMethods.size() > 1) { 232 errorReporter.reportError( 233 builderMethods.iterator().next(), 234 "[AutoValueTwoBuilderConverters] There can be at most one builder converter method"); 235 } 236 this.toBuilderMethods = builderMethods; 237 return builderMethods; 238 } 239 defineVars( AutoValueTemplateVars vars, ImmutableBiMap<ExecutableElement, String> getterToPropertyName)240 void defineVars( 241 AutoValueTemplateVars vars, 242 ImmutableBiMap<ExecutableElement, String> getterToPropertyName) { 243 Iterable<ExecutableElement> builderMethods = abstractMethods(builderTypeElement); 244 boolean autoValueHasToBuilder = !toBuilderMethods.isEmpty(); 245 ImmutableMap<ExecutableElement, TypeMirror> getterToPropertyType = 246 TypeVariables.rewriteReturnTypes( 247 processingEnv.getElementUtils(), 248 processingEnv.getTypeUtils(), 249 getterToPropertyName.keySet(), 250 autoValueClass, 251 builderTypeElement); 252 Optional<BuilderMethodClassifier> optionalClassifier = 253 BuilderMethodClassifier.classify( 254 builderMethods, 255 errorReporter, 256 processingEnv, 257 autoValueClass, 258 builderTypeElement, 259 getterToPropertyName, 260 getterToPropertyType, 261 autoValueHasToBuilder); 262 if (!optionalClassifier.isPresent()) { 263 return; 264 } 265 for (ExecutableElement method : methodsIn(builderTypeElement.getEnclosedElements())) { 266 if (method.getSimpleName().contentEquals("builder") 267 && method.getModifiers().contains(Modifier.STATIC) 268 && method.getAnnotationMirrors().isEmpty()) { 269 // For now we ignore methods with annotations, because for example we do want to allow 270 // Jackson's @JsonCreator. 271 errorReporter.reportWarning( 272 method, 273 "[AutoValueBuilderInBuilder] Static builder() method should be in the containing" 274 + " class"); 275 } 276 } 277 this.classifier = optionalClassifier.get(); 278 Set<ExecutableElement> buildMethods = classifier.buildMethods(); 279 if (buildMethods.size() != 1) { 280 Set<? extends Element> errorElements = 281 buildMethods.isEmpty() ? ImmutableSet.of(builderTypeElement) : buildMethods; 282 for (Element buildMethod : errorElements) { 283 errorReporter.reportError( 284 buildMethod, 285 "[AutoValueBuilderBuild] Builder must have a single no-argument method returning" 286 + " %s%s", 287 autoValueClass, 288 typeParamsString()); 289 } 290 return; 291 } 292 this.buildMethod = Iterables.getOnlyElement(buildMethods); 293 vars.builderIsInterface = builderTypeElement.getKind() == ElementKind.INTERFACE; 294 vars.builderTypeName = TypeSimplifier.classNameOf(builderTypeElement); 295 vars.builderFormalTypes = TypeEncoder.formalTypeParametersString(builderTypeElement); 296 vars.builderActualTypes = TypeSimplifier.actualTypeParametersString(builderTypeElement); 297 vars.buildMethod = Optional.of(new SimpleMethod(buildMethod)); 298 vars.builderGetters = classifier.builderGetters(); 299 vars.builderSetters = classifier.propertyNameToSetters(); 300 301 vars.builderPropertyBuilders = 302 ImmutableMap.copyOf(classifier.propertyNameToPropertyBuilder()); 303 304 Set<Property> required = new LinkedHashSet<>(vars.props); 305 for (Property property : vars.props) { 306 if (property.isNullable() 307 || property.getOptional() != null 308 || vars.builderPropertyBuilders.containsKey(property.getName())) { 309 required.remove(property); 310 } 311 } 312 vars.builderRequiredProperties = ImmutableSet.copyOf(required); 313 } 314 } 315 316 /** 317 * Information about a builder property getter, referenced from the autovalue.vm template. A 318 * property called foo (defined by a method {@code T foo()} or {@code T getFoo()}) can have a 319 * getter method in the builder with the same name ({@code foo()} or {@code getFoo()}) and a 320 * return type of either {@code T} or {@code Optional<T>}. The {@code Optional<T>} form can be 321 * used to tell whether the property has been set. Here, {@code Optional<T>} can be either {@code 322 * java.util.Optional} or {@code com.google.common.base.Optional}. If {@code T} is {@code int}, 323 * {@code long}, or {@code double}, then instead of {@code Optional<T>} we can have {@code 324 * OptionalInt} etc. If {@code T} is a primitive type (including these ones but also the other 325 * five) then {@code Optional<T>} can be the corresponding boxed type. 326 */ 327 public static class PropertyGetter { 328 private final String access; 329 private final String type; 330 private final Optionalish optional; 331 332 /** 333 * Makes a new {@code PropertyGetter} instance. 334 * 335 * @param method the source method which this getter is implementing. 336 * @param type the type that the getter returns. This is written to take imports into account, 337 * so it might be {@code List<String>} for example. It is either identical to the type of 338 * the corresponding getter in the {@code @AutoValue} class, or it is an optional wrapper, 339 * like {@code Optional<List<String>>}. 340 * @param optional a representation of the {@code Optional} type that the getter returns, if 341 * this is an optional getter, or null otherwise. An optional getter is one that returns 342 * {@code Optional<T>} rather than {@code T}, as explained above. 343 */ PropertyGetter(ExecutableElement method, String type, Optionalish optional)344 PropertyGetter(ExecutableElement method, String type, Optionalish optional) { 345 this.access = SimpleMethod.access(method); 346 this.type = type; 347 this.optional = optional; 348 } 349 getAccess()350 public String getAccess() { 351 return access; 352 } 353 getType()354 public String getType() { 355 return type; 356 } 357 getOptional()358 public Optionalish getOptional() { 359 return optional; 360 } 361 } 362 363 /** 364 * Specifies how to copy a parameter value into the target type. This might be the identity, or 365 * it might be something like {@code ImmutableList.of(...)} or {@code Optional.ofNullable(...)}. 366 */ 367 static class Copier { 368 static final Copier IDENTITY = acceptingNull(x -> x); 369 370 private final Function<String, String> copy; 371 private final boolean acceptsNull; 372 Copier(Function<String, String> copy, boolean acceptsNull)373 private Copier(Function<String, String> copy, boolean acceptsNull) { 374 this.copy = copy; 375 this.acceptsNull = acceptsNull; 376 } 377 acceptingNull(Function<String, String> copy)378 static Copier acceptingNull(Function<String, String> copy) { 379 return new Copier(copy, true); 380 } 381 notAcceptingNull(Function<String, String> copy)382 static Copier notAcceptingNull(Function<String, String> copy) { 383 return new Copier(copy, false); 384 } 385 } 386 387 /** 388 * Information about a property setter, referenced from the autovalue.vm template. A property 389 * called foo (defined by a method {@code T foo()} or {@code T getFoo()}) can have a setter method 390 * {@code foo(T)} or {@code setFoo(T)} that returns the builder type. Additionally, it can have a 391 * setter with a type that can be copied to {@code T} through a {@code copyOf} method; for example 392 * a property {@code foo} of type {@code ImmutableSet<String>} can be set with a method {@code 393 * setFoo(Collection<String> foos)}. And, if {@code T} is {@code Optional}, it can have a setter 394 * with a type that can be copied to {@code T} through {@code Optional.of}. 395 */ 396 public static class PropertySetter { 397 private final ExecutableElement setter; 398 private final String access; 399 private final String name; 400 private final String parameterTypeString; 401 private final boolean primitiveParameter; 402 private final String nullableAnnotation; 403 private final Copier copier; 404 PropertySetter(ExecutableElement setter, TypeMirror parameterType, Copier copier)405 PropertySetter(ExecutableElement setter, TypeMirror parameterType, Copier copier) { 406 this.setter = setter; 407 this.copier = copier; 408 this.access = SimpleMethod.access(setter); 409 this.name = setter.getSimpleName().toString(); 410 primitiveParameter = parameterType.getKind().isPrimitive(); 411 this.parameterTypeString = parameterTypeString(setter, parameterType); 412 VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters()); 413 Optional<String> maybeNullable = nullableAnnotationFor(parameterElement, parameterType); 414 this.nullableAnnotation = maybeNullable.orElse(""); 415 } 416 getSetter()417 ExecutableElement getSetter() { 418 return setter; 419 } 420 parameterTypeString(ExecutableElement setter, TypeMirror parameterType)421 private static String parameterTypeString(ExecutableElement setter, TypeMirror parameterType) { 422 if (setter.isVarArgs()) { 423 TypeMirror componentType = MoreTypes.asArray(parameterType).getComponentType(); 424 // This is a bit ugly. It's OK to annotate just the component type, because if it is 425 // say `@Nullable String` then we will end up with `@Nullable String...`. Unlike the 426 // normal array case, we can't have the situation where the array itself is annotated; 427 // you can write `String @Nullable []` to mean that, but you can't write 428 // `String @Nullable ...`. 429 return TypeEncoder.encodeWithAnnotations(componentType) + "..."; 430 } else { 431 return TypeEncoder.encodeWithAnnotations(parameterType); 432 } 433 } 434 getAccess()435 public String getAccess() { 436 return access; 437 } 438 getName()439 public String getName() { 440 return name; 441 } 442 getParameterType()443 public String getParameterType() { 444 return parameterTypeString; 445 } 446 getPrimitiveParameter()447 public boolean getPrimitiveParameter() { 448 return primitiveParameter; 449 } 450 getNullableAnnotation()451 public String getNullableAnnotation() { 452 return nullableAnnotation; 453 } 454 copy(AutoValueProcessor.Property property)455 public String copy(AutoValueProcessor.Property property) { 456 String copy = copier.copy.apply(property.toString()); 457 if (property.isNullable() && !copier.acceptsNull) { 458 copy = String.format("(%s == null ? null : %s)", property, copy); 459 } 460 return copy; 461 } 462 } 463 464 /** 465 * Returns a representation of the given {@code @AutoValue.Builder} class or interface. If the 466 * class or interface has abstract methods that could not be part of any builder, emits error 467 * messages and returns Optional.empty(). 468 */ builderFrom(TypeElement builderTypeElement)469 private Optional<Builder> builderFrom(TypeElement builderTypeElement) { 470 471 // We require the builder to have the same type parameters as the @AutoValue class, meaning the 472 // same names and bounds. In principle the type parameters could have different names, but that 473 // would be confusing, and our code would reject it anyway because it wouldn't consider that 474 // the return type of Foo<U> build() was really the same as the declaration of Foo<T>. This 475 // check produces a better error message in that case and similar ones. 476 477 if (!sameTypeParameters(autoValueClass, builderTypeElement)) { 478 errorReporter.reportError( 479 builderTypeElement, 480 "[AutoValueTypeParamMismatch] Type parameters of %s must have same names and bounds as" 481 + " type parameters of %s", 482 builderTypeElement, 483 autoValueClass); 484 return Optional.empty(); 485 } 486 return Optional.of(new Builder(builderTypeElement)); 487 } 488 sameTypeParameters(TypeElement a, TypeElement b)489 private static boolean sameTypeParameters(TypeElement a, TypeElement b) { 490 int nTypeParameters = a.getTypeParameters().size(); 491 if (nTypeParameters != b.getTypeParameters().size()) { 492 return false; 493 } 494 for (int i = 0; i < nTypeParameters; i++) { 495 TypeParameterElement aParam = a.getTypeParameters().get(i); 496 TypeParameterElement bParam = b.getTypeParameters().get(i); 497 if (!aParam.getSimpleName().equals(bParam.getSimpleName())) { 498 return false; 499 } 500 Set<TypeMirror> autoValueBounds = new TypeMirrorSet(aParam.getBounds()); 501 Set<TypeMirror> builderBounds = new TypeMirrorSet(bParam.getBounds()); 502 if (!autoValueBounds.equals(builderBounds)) { 503 return false; 504 } 505 } 506 return true; 507 } 508 509 /** 510 * Returns a set of all abstract methods in the given TypeElement or inherited from ancestors. If 511 * any of the abstract methods has a return type or parameter type that is not currently defined 512 * then this method will throw an exception that will cause us to defer processing of the current 513 * class until a later annotation-processing round. 514 */ abstractMethods(TypeElement typeElement)515 private ImmutableSet<ExecutableElement> abstractMethods(TypeElement typeElement) { 516 Set<ExecutableElement> methods = 517 getLocalAndInheritedMethods( 518 typeElement, processingEnv.getTypeUtils(), processingEnv.getElementUtils()); 519 ImmutableSet.Builder<ExecutableElement> abstractMethods = ImmutableSet.builder(); 520 for (ExecutableElement method : methods) { 521 if (method.getModifiers().contains(Modifier.ABSTRACT)) { 522 MissingTypes.deferIfMissingTypesIn(method); 523 abstractMethods.add(method); 524 } 525 } 526 return abstractMethods.build(); 527 } 528 typeParamsString()529 private String typeParamsString() { 530 return TypeSimplifier.actualTypeParametersString(autoValueClass); 531 } 532 } 533