1 /* 2 * Copyright 2013 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.factory.processor; 17 18 import static com.google.auto.common.MoreTypes.asElement; 19 import static com.google.auto.common.MoreTypes.asTypeElement; 20 import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE; 21 import static javax.lang.model.type.TypeKind.DECLARED; 22 import static javax.lang.model.type.TypeKind.ERROR; 23 import static javax.lang.model.util.ElementFilter.methodsIn; 24 25 import com.google.auto.common.MoreTypes; 26 import com.google.auto.factory.AutoFactory; 27 import com.google.auto.factory.AutoFactory.AnnotationsToApply; 28 import com.google.auto.factory.Provided; 29 import com.google.auto.service.AutoService; 30 import com.google.common.base.Throwables; 31 import com.google.common.collect.ImmutableList; 32 import com.google.common.collect.ImmutableListMultimap; 33 import com.google.common.collect.ImmutableSet; 34 import com.google.common.collect.ImmutableSetMultimap; 35 import com.google.common.collect.ImmutableSortedSet; 36 import com.google.common.collect.Iterables; 37 import java.io.IOException; 38 import java.util.Arrays; 39 import java.util.Comparator; 40 import java.util.HashSet; 41 import java.util.List; 42 import java.util.Optional; 43 import java.util.Set; 44 import javax.annotation.processing.AbstractProcessor; 45 import javax.annotation.processing.Messager; 46 import javax.annotation.processing.ProcessingEnvironment; 47 import javax.annotation.processing.Processor; 48 import javax.annotation.processing.RoundEnvironment; 49 import javax.lang.model.SourceVersion; 50 import javax.lang.model.element.AnnotationMirror; 51 import javax.lang.model.element.Element; 52 import javax.lang.model.element.ExecutableElement; 53 import javax.lang.model.element.Modifier; 54 import javax.lang.model.element.TypeElement; 55 import javax.lang.model.type.ExecutableType; 56 import javax.lang.model.type.TypeMirror; 57 import javax.lang.model.util.ElementFilter; 58 import javax.lang.model.util.Elements; 59 import javax.lang.model.util.Types; 60 import javax.tools.Diagnostic.Kind; 61 import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; 62 import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType; 63 64 /** 65 * The annotation processor that generates factories for {@link AutoFactory} annotations. 66 * 67 * @author Gregory Kick 68 */ 69 @IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING) 70 @AutoService(Processor.class) 71 public final class AutoFactoryProcessor extends AbstractProcessor { 72 private FactoryDescriptorGenerator factoryDescriptorGenerator; 73 private AutoFactoryDeclaration.Factory declarationFactory; 74 private ProvidedChecker providedChecker; 75 private Messager messager; 76 private Elements elements; 77 private Types types; 78 79 @Override init(ProcessingEnvironment processingEnv)80 public synchronized void init(ProcessingEnvironment processingEnv) { 81 super.init(processingEnv); 82 elements = processingEnv.getElementUtils(); 83 types = processingEnv.getTypeUtils(); 84 messager = processingEnv.getMessager(); 85 providedChecker = new ProvidedChecker(messager); 86 declarationFactory = new AutoFactoryDeclaration.Factory(elements, messager); 87 factoryDescriptorGenerator = 88 new FactoryDescriptorGenerator(messager, types, declarationFactory); 89 } 90 91 @Override process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)92 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 93 try { 94 doProcess(roundEnv); 95 } catch (Throwable e) { 96 messager.printMessage( 97 Kind.ERROR, 98 "Failed to process @AutoFactory annotations:\n" + Throwables.getStackTraceAsString(e)); 99 } 100 return false; 101 } 102 doProcess(RoundEnvironment roundEnv)103 private void doProcess(RoundEnvironment roundEnv) { 104 for (Element element : roundEnv.getElementsAnnotatedWith(Provided.class)) { 105 providedChecker.checkProvidedParameter(element); 106 } 107 108 for (Element element : 109 roundEnv.getElementsAnnotatedWith(AnnotationsToApply.class)) { 110 checkAnnotationsToApply(element); 111 } 112 113 ImmutableListMultimap.Builder<PackageAndClass, FactoryMethodDescriptor> indexedMethodsBuilder = 114 ImmutableListMultimap.builder(); 115 ImmutableSetMultimap.Builder<PackageAndClass, ImplementationMethodDescriptor> 116 implementationMethodDescriptorsBuilder = ImmutableSetMultimap.builder(); 117 // Iterate over the classes and constructors that are annotated with @AutoFactory. 118 for (Element element : roundEnv.getElementsAnnotatedWith(AutoFactory.class)) { 119 Optional<AutoFactoryDeclaration> declaration = declarationFactory.createIfValid(element); 120 if (declaration.isPresent()) { 121 PackageAndClass factoryName = declaration.get().getFactoryName(); 122 TypeElement extendingType = declaration.get().extendingType(); 123 implementationMethodDescriptorsBuilder.putAll( 124 factoryName, implementationMethods(extendingType, element)); 125 for (TypeElement implementingType : declaration.get().implementingTypes()) { 126 implementationMethodDescriptorsBuilder.putAll( 127 factoryName, implementationMethods(implementingType, element)); 128 } 129 } 130 131 ImmutableSet<FactoryMethodDescriptor> descriptors = 132 factoryDescriptorGenerator.generateDescriptor(element); 133 for (FactoryMethodDescriptor descriptor : descriptors) { 134 indexedMethodsBuilder.put(descriptor.factoryName(), descriptor); 135 } 136 } 137 138 ImmutableSetMultimap<PackageAndClass, ImplementationMethodDescriptor> 139 implementationMethodDescriptors = implementationMethodDescriptorsBuilder.build(); 140 ImmutableListMultimap<PackageAndClass, FactoryMethodDescriptor> indexedMethods = 141 indexedMethodsBuilder.build(); 142 ImmutableSetMultimap<String, PackageAndClass> factoriesBeingCreated = 143 simpleNamesToNames(indexedMethods.keySet()); 144 FactoryWriter factoryWriter = new FactoryWriter(processingEnv, factoriesBeingCreated); 145 146 indexedMethods 147 .asMap() 148 .forEach( 149 (factoryName, methodDescriptors) -> { 150 if (methodDescriptors.isEmpty()) { 151 // This shouldn't happen, but check anyway to avoid an exception for 152 // methodDescriptors.iterator().next() below. 153 return; 154 } 155 // Sort to ensure output is deterministic. 156 ImmutableSortedSet.Builder<AnnotationMirror> annotationsBuilder = 157 ImmutableSortedSet.orderedBy(ANNOTATION_COMPARATOR); 158 // The sets of classes that are mentioned in the `extending` and `implementing` 159 // elements, respectively, of the @AutoFactory annotations for this factory. 160 ImmutableSet.Builder<TypeMirror> extending = newTypeSetBuilder(); 161 ImmutableSortedSet.Builder<TypeMirror> implementing = newTypeSetBuilder(); 162 boolean publicType = false; 163 Set<Boolean> allowSubclassesSet = new HashSet<>(); 164 boolean skipCreation = false; 165 for (FactoryMethodDescriptor methodDescriptor : methodDescriptors) { 166 annotationsBuilder.addAll(methodDescriptor.declaration().annotations()); 167 extending.add(methodDescriptor.declaration().extendingType().asType()); 168 for (TypeElement implementingType : 169 methodDescriptor.declaration().implementingTypes()) { 170 implementing.add(implementingType.asType()); 171 } 172 publicType |= methodDescriptor.publicMethod(); 173 allowSubclassesSet.add(methodDescriptor.declaration().allowSubclasses()); 174 if (allowSubclassesSet.size() > 1) { 175 skipCreation = true; 176 messager.printMessage( 177 Kind.ERROR, 178 "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.", 179 methodDescriptor.declaration().target(), 180 methodDescriptor.declaration().mirror(), 181 methodDescriptor.declaration().valuesMap().get("allowSubclasses")); 182 } 183 } 184 // The set can't be empty because we eliminated methodDescriptors.isEmpty() above. 185 boolean allowSubclasses = allowSubclassesSet.iterator().next(); 186 if (!skipCreation) { 187 try { 188 factoryWriter.writeFactory( 189 FactoryDescriptor.create( 190 factoryName, 191 annotationsBuilder.build(), 192 Iterables.getOnlyElement(extending.build()), 193 implementing.build(), 194 publicType, 195 ImmutableSet.copyOf(methodDescriptors), 196 implementationMethodDescriptors.get(factoryName), 197 allowSubclasses)); 198 } catch (IOException e) { 199 messager.printMessage(Kind.ERROR, "failed: " + e); 200 } 201 } 202 }); 203 } 204 205 private static final Comparator<AnnotationMirror> ANNOTATION_COMPARATOR = 206 Comparator.comparing(mirror -> mirror.getAnnotationType().toString()); 207 implementationMethods( TypeElement supertype, Element autoFactoryElement)208 private ImmutableSet<ImplementationMethodDescriptor> implementationMethods( 209 TypeElement supertype, Element autoFactoryElement) { 210 ImmutableSet.Builder<ImplementationMethodDescriptor> implementationMethodsBuilder = 211 ImmutableSet.builder(); 212 for (ExecutableElement implementationMethod : 213 ElementFilter.methodsIn(elements.getAllMembers(supertype))) { 214 if (implementationMethod.getModifiers().contains(Modifier.ABSTRACT)) { 215 ExecutableType methodType = 216 Elements2.getExecutableElementAsMemberOf(types, implementationMethod, supertype); 217 ImmutableSet<Parameter> passedParameters = 218 Parameter.forParameterList( 219 implementationMethod.getParameters(), methodType.getParameterTypes(), types); 220 implementationMethodsBuilder.add( 221 ImplementationMethodDescriptor.builder() 222 .name(implementationMethod.getSimpleName().toString()) 223 .returnType(getAnnotatedType(autoFactoryElement)) 224 .publicMethod() 225 .passedParameters(passedParameters) 226 .isVarArgs(implementationMethod.isVarArgs()) 227 .exceptions(implementationMethod.getThrownTypes()) 228 .build()); 229 } 230 } 231 return implementationMethodsBuilder.build(); 232 } 233 getAnnotatedType(Element element)234 private TypeMirror getAnnotatedType(Element element) { 235 List<TypeElement> types = ImmutableList.of(); 236 while (types.isEmpty()) { 237 types = ElementFilter.typesIn(Arrays.asList(element)); 238 element = element.getEnclosingElement(); 239 } 240 return Iterables.getOnlyElement(types).asType(); 241 } 242 simpleNamesToNames( ImmutableSet<PackageAndClass> names)243 private static ImmutableSetMultimap<String, PackageAndClass> simpleNamesToNames( 244 ImmutableSet<PackageAndClass> names) { 245 // .collect(toImmutableSetMultimap(...)) would make this much simpler but ran into problems in 246 // Google's internal build system because of multiple Guava versions. 247 ImmutableSetMultimap.Builder<String, PackageAndClass> builder = ImmutableSetMultimap.builder(); 248 for (PackageAndClass name : names) { 249 builder.put(name.className(), name); 250 } 251 return builder.build(); 252 } 253 newTypeSetBuilder()254 private static ImmutableSortedSet.Builder<TypeMirror> newTypeSetBuilder() { 255 return ImmutableSortedSet.orderedBy( 256 Comparator.comparing(t -> MoreTypes.asTypeElement(t).getQualifiedName().toString())); 257 } 258 259 /** Checks that {@link AnnotationsToApply} is used correctly. */ checkAnnotationsToApply(Element annotation)260 private void checkAnnotationsToApply(Element annotation) { 261 if (!annotation.getKind().equals(ANNOTATION_TYPE)) { 262 // Should not be possible because of @Target. 263 messager.printMessage( 264 Kind.ERROR, 265 "@" 266 + AnnotationsToApply.class.getSimpleName() 267 + " must be applied to an annotation type declaration.", 268 annotation); 269 } 270 Set<TypeElement> seenAnnotations = new HashSet<>(); 271 for (ExecutableElement annotationMember : methodsIn(annotation.getEnclosedElements())) { 272 TypeMirror memberType = annotationMember.getReturnType(); 273 boolean isAnnotation = memberType.getKind().equals(DECLARED) && asElement(memberType).getKind().equals(ANNOTATION_TYPE); 274 if (!isAnnotation && !memberType.getKind().equals(ERROR)) { 275 messager.printMessage( 276 Kind.ERROR, 277 "Members of an @" 278 + AnnotationsToApply.class.getSimpleName() 279 + " annotation must themselves be annotations; " 280 + annotationMember.getSimpleName() 281 + " has type " 282 + memberType, 283 annotationMember); 284 } else { 285 TypeElement annotationElement = asTypeElement(memberType); 286 if (!seenAnnotations.add(annotationElement)) { 287 messager.printMessage( 288 Kind.ERROR, "More than one @" + annotationElement + " in " + annotation, annotation); 289 } 290 } 291 } 292 } 293 294 @Override getSupportedAnnotationTypes()295 public ImmutableSet<String> getSupportedAnnotationTypes() { 296 return ImmutableSet.of( 297 AutoFactory.class.getCanonicalName(), 298 Provided.class.getCanonicalName(), 299 AnnotationsToApply.class.getCanonicalName()); 300 } 301 302 @Override getSupportedSourceVersion()303 public SourceVersion getSupportedSourceVersion() { 304 return SourceVersion.latestSupported(); 305 } 306 } 307