1 /* 2 * Copyright 2012 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.common.MoreStreams.toImmutableList; 20 import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_NAME; 21 import static com.google.common.collect.Sets.difference; 22 import static com.google.common.collect.Sets.intersection; 23 import static java.util.Comparator.naturalOrder; 24 import static java.util.stream.Collectors.joining; 25 26 import com.google.auto.service.AutoService; 27 import com.google.auto.value.extension.AutoValueExtension; 28 import com.google.common.annotations.VisibleForTesting; 29 import com.google.common.base.Strings; 30 import com.google.common.base.Throwables; 31 import com.google.common.collect.ImmutableBiMap; 32 import com.google.common.collect.ImmutableList; 33 import com.google.common.collect.ImmutableListMultimap; 34 import com.google.common.collect.ImmutableMap; 35 import com.google.common.collect.ImmutableSet; 36 import java.lang.annotation.Annotation; 37 import java.util.ArrayList; 38 import java.util.Collections; 39 import java.util.HashSet; 40 import java.util.List; 41 import java.util.Optional; 42 import java.util.ServiceConfigurationError; 43 import java.util.Set; 44 import javax.annotation.processing.ProcessingEnvironment; 45 import javax.annotation.processing.Processor; 46 import javax.annotation.processing.SupportedAnnotationTypes; 47 import javax.lang.model.element.AnnotationMirror; 48 import javax.lang.model.element.ElementKind; 49 import javax.lang.model.element.ExecutableElement; 50 import javax.lang.model.element.TypeElement; 51 import javax.lang.model.type.TypeKind; 52 import javax.lang.model.type.TypeMirror; 53 import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; 54 import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType; 55 56 /** 57 * Javac annotation processor (compiler plugin) for value types; user code never references this 58 * class. 59 * 60 * @see <a href="https://github.com/google/auto/tree/master/value">AutoValue User's Guide</a> 61 * @author Éamonn McManus 62 */ 63 @AutoService(Processor.class) 64 @SupportedAnnotationTypes(AUTO_VALUE_NAME) 65 @IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.DYNAMIC) 66 public class AutoValueProcessor extends AutoValueishProcessor { 67 static final String OMIT_IDENTIFIERS_OPTION = "com.google.auto.value.OmitIdentifiers"; 68 69 // We moved MemoizeExtension to a different package, which had an unexpected effect: 70 // now if an old version of AutoValue is in the class path, ServiceLoader can pick up both the 71 // old and the new versions of MemoizeExtension. So we exclude the old version if we see it. 72 // The new version will be bundled with this processor so we should always find it. 73 private static final String OLD_MEMOIZE_EXTENSION = 74 "com.google.auto.value.extension.memoized.MemoizeExtension"; 75 AutoValueProcessor()76 public AutoValueProcessor() { 77 this(AutoValueProcessor.class.getClassLoader()); 78 } 79 80 @VisibleForTesting AutoValueProcessor(ClassLoader loaderForExtensions)81 AutoValueProcessor(ClassLoader loaderForExtensions) { 82 super(AUTO_VALUE_NAME); 83 this.extensions = null; 84 this.loaderForExtensions = loaderForExtensions; 85 } 86 87 @VisibleForTesting AutoValueProcessor(Iterable<? extends AutoValueExtension> extensions)88 public AutoValueProcessor(Iterable<? extends AutoValueExtension> extensions) { 89 super(AUTO_VALUE_NAME); 90 this.extensions = ImmutableList.copyOf(extensions); 91 this.loaderForExtensions = null; 92 } 93 94 // Depending on how this AutoValueProcessor was constructed, we might already have a list of 95 // extensions when init() is run, or, if `extensions` is null, we have a ClassLoader that will be 96 // used to get the list using the ServiceLoader API. 97 private ImmutableList<AutoValueExtension> extensions; 98 private final ClassLoader loaderForExtensions; 99 100 @VisibleForTesting extensionsFromLoader(ClassLoader loader)101 static ImmutableList<AutoValueExtension> extensionsFromLoader(ClassLoader loader) { 102 return SimpleServiceLoader.load(AutoValueExtension.class, loader).stream() 103 .filter(ext -> !ext.getClass().getName().equals(OLD_MEMOIZE_EXTENSION)) 104 .collect(toImmutableList()); 105 } 106 107 @Override init(ProcessingEnvironment processingEnv)108 public synchronized void init(ProcessingEnvironment processingEnv) { 109 super.init(processingEnv); 110 111 if (extensions == null) { 112 try { 113 extensions = extensionsFromLoader(loaderForExtensions); 114 } catch (RuntimeException | Error e) { 115 String explain = 116 (e instanceof ServiceConfigurationError) 117 ? " This may be due to a corrupt jar file in the compiler's classpath." 118 : ""; 119 errorReporter() 120 .reportWarning( 121 null, 122 "[AutoValueExtensionsException] An exception occurred while looking for AutoValue" 123 + " extensions. No extensions will function.%s\n%s", 124 explain, 125 Throwables.getStackTraceAsString(e)); 126 extensions = ImmutableList.of(); 127 } 128 } 129 } 130 131 @Override getSupportedOptions()132 public ImmutableSet<String> getSupportedOptions() { 133 ImmutableSet.Builder<String> builder = ImmutableSet.builder(); 134 AutoValueExtension.IncrementalExtensionType incrementalType = 135 extensions.stream() 136 .map(e -> e.incrementalType(processingEnv)) 137 .min(naturalOrder()) 138 .orElse(AutoValueExtension.IncrementalExtensionType.ISOLATING); 139 builder 140 .add(OMIT_IDENTIFIERS_OPTION) 141 .add(Nullables.NULLABLE_OPTION) 142 .addAll(optionsFor(incrementalType)); 143 for (AutoValueExtension extension : extensions) { 144 builder.addAll(extension.getSupportedOptions()); 145 } 146 return builder.build(); 147 } 148 optionsFor( AutoValueExtension.IncrementalExtensionType incrementalType)149 private static ImmutableSet<String> optionsFor( 150 AutoValueExtension.IncrementalExtensionType incrementalType) { 151 switch (incrementalType) { 152 case ISOLATING: 153 return ImmutableSet.of(IncrementalAnnotationProcessorType.ISOLATING.getProcessorOption()); 154 case AGGREGATING: 155 return ImmutableSet.of(IncrementalAnnotationProcessorType.AGGREGATING.getProcessorOption()); 156 case UNKNOWN: 157 return ImmutableSet.of(); 158 } 159 throw new AssertionError(incrementalType); 160 } 161 generatedSubclassName(TypeElement type, int depth)162 static String generatedSubclassName(TypeElement type, int depth) { 163 return generatedClassName(type, Strings.repeat("$", depth) + "AutoValue_"); 164 } 165 166 @Override processType(TypeElement type)167 void processType(TypeElement type) { 168 if (type.getKind() != ElementKind.CLASS) { 169 errorReporter() 170 .abortWithError(type, "[AutoValueNotClass] @AutoValue only applies to classes"); 171 } 172 if (ancestorIsAutoValue(type)) { 173 errorReporter() 174 .abortWithError(type, "[AutoValueExtend] One @AutoValue class may not extend another"); 175 } 176 if (implementsAnnotation(type)) { 177 errorReporter() 178 .abortWithError( 179 type, 180 "[AutoValueImplAnnotation] @AutoValue may not be used to implement an annotation" 181 + " interface; try using @AutoAnnotation instead"); 182 } 183 checkModifiersIfNested(type); 184 185 // We are going to classify the methods of the @AutoValue class into several categories. 186 // This covers the methods in the class itself and the ones it inherits from supertypes. 187 // First, the only concrete (non-abstract) methods we are interested in are overrides of 188 // Object methods (equals, hashCode, toString), which signal that we should not generate 189 // an implementation of those methods. 190 // Then, each abstract method is one of the following: 191 // (1) A property getter, like "abstract String foo()" or "abstract String getFoo()". 192 // (2) A toBuilder() method, which is any abstract no-arg method returning the Builder for 193 // this @AutoValue class. 194 // (3) An abstract method that will be consumed by an extension, such as 195 // Parcelable.describeContents() or Parcelable.writeToParcel(Parcel, int). 196 // The describeContents() example shows a quirk here: initially we will identify it as a 197 // property, which means that we need to reconstruct the list of properties after allowing 198 // extensions to consume abstract methods. 199 // If there are abstract methods that don't fit any of the categories above, that is an error 200 // which we signal explicitly to avoid confusion. 201 202 ImmutableSet<ExecutableElement> methods = 203 getLocalAndInheritedMethods( 204 type, processingEnv.getTypeUtils(), processingEnv.getElementUtils()); 205 ImmutableSet<ExecutableElement> abstractMethods = abstractMethodsIn(methods); 206 207 BuilderSpec builderSpec = new BuilderSpec(type, processingEnv, errorReporter()); 208 Optional<BuilderSpec.Builder> builder = builderSpec.getBuilder(); 209 ImmutableSet<ExecutableElement> toBuilderMethods; 210 if (builder.isPresent()) { 211 toBuilderMethods = builder.get().toBuilderMethods(typeUtils(), type, abstractMethods); 212 } else { 213 toBuilderMethods = ImmutableSet.of(); 214 } 215 216 ImmutableMap<ExecutableElement, TypeMirror> propertyMethodsAndTypes = 217 propertyMethodsIn(immutableSetDifference(abstractMethods, toBuilderMethods), type); 218 ImmutableMap<String, ExecutableElement> properties = 219 propertyNameToMethodMap(propertyMethodsAndTypes.keySet()); 220 221 ExtensionContext context = 222 new ExtensionContext( 223 processingEnv, type, properties, propertyMethodsAndTypes, abstractMethods); 224 ImmutableList<AutoValueExtension> applicableExtensions = applicableExtensions(type, context); 225 ImmutableSet<ExecutableElement> consumedMethods = 226 methodsConsumedByExtensions( 227 type, applicableExtensions, context, abstractMethods, properties); 228 229 if (!consumedMethods.isEmpty()) { 230 ImmutableSet<ExecutableElement> allAbstractMethods = abstractMethods; 231 abstractMethods = immutableSetDifference(abstractMethods, consumedMethods); 232 toBuilderMethods = immutableSetDifference(toBuilderMethods, consumedMethods); 233 propertyMethodsAndTypes = 234 propertyMethodsIn(immutableSetDifference(abstractMethods, toBuilderMethods), type); 235 properties = propertyNameToMethodMap(propertyMethodsAndTypes.keySet()); 236 context = 237 new ExtensionContext( 238 processingEnv, type, properties, propertyMethodsAndTypes, allAbstractMethods); 239 } 240 241 ImmutableSet<ExecutableElement> propertyMethods = propertyMethodsAndTypes.keySet(); 242 boolean extensionsPresent = !applicableExtensions.isEmpty(); 243 validateMethods(type, abstractMethods, toBuilderMethods, propertyMethods, extensionsPresent); 244 245 String finalSubclass = TypeSimplifier.simpleNameOf(generatedSubclassName(type, 0)); 246 AutoValueTemplateVars vars = new AutoValueTemplateVars(); 247 vars.types = processingEnv.getTypeUtils(); 248 vars.identifiers = !processingEnv.getOptions().containsKey(OMIT_IDENTIFIERS_OPTION); 249 defineSharedVarsForType(type, methods, vars); 250 defineVarsForType(type, vars, toBuilderMethods, propertyMethodsAndTypes, builder); 251 vars.builtType = vars.origClass + vars.actualTypes; 252 vars.build = "new " + finalSubclass + vars.actualTypes; 253 254 // If we've encountered problems then we might end up invoking extensions with inconsistent 255 // state. Anyway we probably don't want to generate code which is likely to provoke further 256 // compile errors to add to the ones we've already seen. 257 errorReporter().abortIfAnyError(); 258 259 GwtCompatibility gwtCompatibility = new GwtCompatibility(type); 260 vars.gwtCompatibleAnnotation = gwtCompatibility.gwtCompatibleAnnotationString(); 261 262 builder.ifPresent(context::setBuilderContext); 263 int subclassDepth = writeExtensions(type, context, applicableExtensions); 264 String subclass = generatedSubclassName(type, subclassDepth); 265 vars.subclass = TypeSimplifier.simpleNameOf(subclass); 266 vars.isFinal = (subclassDepth == 0); 267 vars.modifiers = vars.isFinal ? "final " : "abstract "; 268 269 String text = vars.toText(); 270 text = TypeEncoder.decode(text, processingEnv, vars.pkg, type.asType()); 271 text = Reformatter.fixup(text); 272 writeSourceFile(subclass, text, type); 273 GwtSerialization gwtSerialization = new GwtSerialization(gwtCompatibility, processingEnv, type); 274 gwtSerialization.maybeWriteGwtSerializer(vars, finalSubclass); 275 } 276 277 // Invokes each of the given extensions to generate its subclass, and returns the number of 278 // hierarchy classes that extensions generated. This number is then the number of $ characters 279 // that should precede the name of the AutoValue implementation class. 280 // Assume the @AutoValue class is com.example.Foo.Bar. Then if there are no 281 // extensions the returned value will be 0, so the AutoValue implementation will be 282 // com.example.AutoValue_Foo_Bar. If there is one extension, it will be asked to 283 // generate AutoValue_Foo_Bar with parent $AutoValue_Foo_Bar. If it does so (returns 284 // non-null) then the returned value will be 1, so the AutoValue implementation will be 285 // com.example.$AutoValue_Foo_Bar. Otherwise, the returned value will still be 0. Likewise, 286 // if there is a second extension and both extensions return non-null, the first one will 287 // generate AutoValue_Foo_Bar with parent $AutoValue_Foo_Bar, the second will generate 288 // $AutoValue_Foo_Bar with parent $$AutoValue_Foo_Bar, and the returned value will be 2 for 289 // com.example.$$AutoValue_Foo_Bar. writeExtensions( TypeElement type, ExtensionContext context, ImmutableList<AutoValueExtension> applicableExtensions)290 private int writeExtensions( 291 TypeElement type, 292 ExtensionContext context, 293 ImmutableList<AutoValueExtension> applicableExtensions) { 294 int writtenSoFar = 0; 295 for (AutoValueExtension extension : applicableExtensions) { 296 String parentFqName = generatedSubclassName(type, writtenSoFar + 1); 297 String parentSimpleName = TypeSimplifier.simpleNameOf(parentFqName); 298 String classFqName = generatedSubclassName(type, writtenSoFar); 299 String classSimpleName = TypeSimplifier.simpleNameOf(classFqName); 300 boolean isFinal = (writtenSoFar == 0); 301 String source = extension.generateClass(context, classSimpleName, parentSimpleName, isFinal); 302 if (source != null) { 303 source = Reformatter.fixup(source); 304 writeSourceFile(classFqName, source, type); 305 writtenSoFar++; 306 } 307 } 308 return writtenSoFar; 309 } 310 applicableExtensions( TypeElement type, ExtensionContext context)311 private ImmutableList<AutoValueExtension> applicableExtensions( 312 TypeElement type, ExtensionContext context) { 313 List<AutoValueExtension> applicableExtensions = new ArrayList<>(); 314 List<AutoValueExtension> finalExtensions = new ArrayList<>(); 315 for (AutoValueExtension extension : extensions) { 316 if (extension.applicable(context)) { 317 if (extension.mustBeFinal(context)) { 318 finalExtensions.add(extension); 319 } else { 320 applicableExtensions.add(extension); 321 } 322 } 323 } 324 switch (finalExtensions.size()) { 325 case 0: 326 break; 327 case 1: 328 applicableExtensions.add(0, finalExtensions.get(0)); 329 break; 330 default: 331 errorReporter() 332 .reportError( 333 type, 334 "[AutoValueMultiFinal] More than one extension wants to generate the final class:" 335 + " %s", 336 finalExtensions.stream().map(this::extensionName).collect(joining(", "))); 337 break; 338 } 339 return ImmutableList.copyOf(applicableExtensions); 340 } 341 methodsConsumedByExtensions( TypeElement type, ImmutableList<AutoValueExtension> applicableExtensions, ExtensionContext context, ImmutableSet<ExecutableElement> abstractMethods, ImmutableMap<String, ExecutableElement> properties)342 private ImmutableSet<ExecutableElement> methodsConsumedByExtensions( 343 TypeElement type, 344 ImmutableList<AutoValueExtension> applicableExtensions, 345 ExtensionContext context, 346 ImmutableSet<ExecutableElement> abstractMethods, 347 ImmutableMap<String, ExecutableElement> properties) { 348 Set<ExecutableElement> consumed = new HashSet<>(); 349 for (AutoValueExtension extension : applicableExtensions) { 350 Set<ExecutableElement> consumedHere = new HashSet<>(); 351 for (String consumedProperty : extension.consumeProperties(context)) { 352 ExecutableElement propertyMethod = properties.get(consumedProperty); 353 if (propertyMethod == null) { 354 errorReporter() 355 .reportError( 356 type, 357 "[AutoValueConsumeNonexist] Extension %s wants to consume a property that does" 358 + " not exist: %s", 359 extensionName(extension), 360 consumedProperty); 361 } else { 362 consumedHere.add(propertyMethod); 363 } 364 } 365 for (ExecutableElement consumedMethod : extension.consumeMethods(context)) { 366 if (!abstractMethods.contains(consumedMethod)) { 367 errorReporter() 368 .reportError( 369 type, 370 "[AutoValueConsumeNotAbstract] Extension %s wants to consume a method that is" 371 + " not one of the abstract methods in this class: %s", 372 extensionName(extension), 373 consumedMethod); 374 } else { 375 consumedHere.add(consumedMethod); 376 } 377 } 378 for (ExecutableElement repeat : intersection(consumed, consumedHere)) { 379 errorReporter() 380 .reportError( 381 repeat, 382 "[AutoValueMultiConsume] Extension %s wants to consume a method that was already" 383 + " consumed by another extension", 384 extensionName(extension)); 385 } 386 consumed.addAll(consumedHere); 387 } 388 return ImmutableSet.copyOf(consumed); 389 } 390 validateMethods( TypeElement type, ImmutableSet<ExecutableElement> abstractMethods, ImmutableSet<ExecutableElement> toBuilderMethods, ImmutableSet<ExecutableElement> propertyMethods, boolean extensionsPresent)391 private void validateMethods( 392 TypeElement type, 393 ImmutableSet<ExecutableElement> abstractMethods, 394 ImmutableSet<ExecutableElement> toBuilderMethods, 395 ImmutableSet<ExecutableElement> propertyMethods, 396 boolean extensionsPresent) { 397 for (ExecutableElement method : abstractMethods) { 398 if (propertyMethods.contains(method)) { 399 checkReturnType(type, method); 400 } else if (!toBuilderMethods.contains(method) 401 && objectMethodToOverride(method) == ObjectMethod.NONE) { 402 // This could reasonably be an error, were it not for an Eclipse bug in 403 // ElementUtils.override that sometimes fails to recognize that one method overrides 404 // another, and therefore leaves us with both an abstract method and the subclass method 405 // that overrides it. This shows up in AutoValueTest.LukesBase for example. 406 String extensionMessage = extensionsPresent ? ", and no extension consumed it" : ""; 407 errorReporter() 408 .reportWarning( 409 method, 410 "[AutoValueBuilderWhat] Abstract method is neither a property getter nor a Builder" 411 + " converter%s", 412 extensionMessage); 413 } 414 } 415 errorReporter().abortIfAnyError(); 416 } 417 extensionName(AutoValueExtension extension)418 private String extensionName(AutoValueExtension extension) { 419 return extension.getClass().getName(); 420 } 421 defineVarsForType( TypeElement type, AutoValueTemplateVars vars, ImmutableSet<ExecutableElement> toBuilderMethods, ImmutableMap<ExecutableElement, TypeMirror> propertyMethodsAndTypes, Optional<BuilderSpec.Builder> maybeBuilder)422 private void defineVarsForType( 423 TypeElement type, 424 AutoValueTemplateVars vars, 425 ImmutableSet<ExecutableElement> toBuilderMethods, 426 ImmutableMap<ExecutableElement, TypeMirror> propertyMethodsAndTypes, 427 Optional<BuilderSpec.Builder> maybeBuilder) { 428 ImmutableSet<ExecutableElement> propertyMethods = propertyMethodsAndTypes.keySet(); 429 vars.toBuilderMethods = 430 toBuilderMethods.stream().map(SimpleMethod::new).collect(toImmutableList()); 431 vars.toBuilderConstructor = !vars.toBuilderMethods.isEmpty(); 432 ImmutableListMultimap<ExecutableElement, AnnotationMirror> annotatedPropertyFields = 433 propertyFieldAnnotationMap(type, propertyMethods); 434 ImmutableListMultimap<ExecutableElement, AnnotationMirror> annotatedPropertyMethods = 435 propertyMethodAnnotationMap(type, propertyMethods); 436 vars.props = 437 propertySet(propertyMethodsAndTypes, annotatedPropertyFields, annotatedPropertyMethods); 438 // Check for @AutoValue.Builder and add appropriate variables if it is present. 439 maybeBuilder.ifPresent( 440 builder -> { 441 ImmutableBiMap<ExecutableElement, String> methodToPropertyName = 442 propertyNameToMethodMap(propertyMethods).inverse(); 443 builder.defineVarsForAutoValue(vars, methodToPropertyName); 444 vars.builderName = "Builder"; 445 vars.builderAnnotations = copiedClassAnnotations(builder.builderType()); 446 }); 447 } 448 449 @Override nullableAnnotationForMethod(ExecutableElement propertyMethod)450 Optional<String> nullableAnnotationForMethod(ExecutableElement propertyMethod) { 451 return nullableAnnotationFor(propertyMethod, propertyMethod.getReturnType()); 452 } 453 prefixedGettersIn(Iterable<ExecutableElement> methods)454 static ImmutableSet<ExecutableElement> prefixedGettersIn(Iterable<ExecutableElement> methods) { 455 ImmutableSet.Builder<ExecutableElement> getters = ImmutableSet.builder(); 456 for (ExecutableElement method : methods) { 457 String name = method.getSimpleName().toString(); 458 // TODO(emcmanus): decide whether getfoo() (without a capital) is a getter. Currently it is. 459 boolean get = name.startsWith("get") && !name.equals("get"); 460 boolean is = 461 name.startsWith("is") 462 && !name.equals("is") 463 && method.getReturnType().getKind() == TypeKind.BOOLEAN; 464 if (get || is) { 465 getters.add(method); 466 } 467 } 468 return getters.build(); 469 } 470 ancestorIsAutoValue(TypeElement type)471 private boolean ancestorIsAutoValue(TypeElement type) { 472 while (true) { 473 TypeMirror parentMirror = type.getSuperclass(); 474 if (parentMirror.getKind() == TypeKind.NONE) { 475 return false; 476 } 477 TypeElement parentElement = (TypeElement) typeUtils().asElement(parentMirror); 478 if (hasAnnotationMirror(parentElement, AUTO_VALUE_NAME)) { 479 return true; 480 } 481 type = parentElement; 482 } 483 } 484 implementsAnnotation(TypeElement type)485 private boolean implementsAnnotation(TypeElement type) { 486 return typeUtils().isAssignable(type.asType(), getTypeMirror(Annotation.class)); 487 } 488 getTypeMirror(Class<?> c)489 private TypeMirror getTypeMirror(Class<?> c) { 490 return processingEnv.getElementUtils().getTypeElement(c.getName()).asType(); 491 } 492 immutableSetDifference(ImmutableSet<E> a, ImmutableSet<E> b)493 private static <E> ImmutableSet<E> immutableSetDifference(ImmutableSet<E> a, ImmutableSet<E> b) { 494 if (Collections.disjoint(a, b)) { 495 return a; 496 } else { 497 return ImmutableSet.copyOf(difference(a, b)); 498 } 499 } 500 } 501