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