1 /* 2 * Copyright (C) 2019 The Dagger Authors. 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 17 package dagger.hilt.processor.internal.aggregateddeps; 18 19 import static com.google.auto.common.AnnotationMirrors.getAnnotationValue; 20 import static com.google.auto.common.MoreElements.asType; 21 import static com.google.auto.common.MoreElements.getPackage; 22 import static com.google.common.collect.Iterables.getOnlyElement; 23 import static dagger.hilt.processor.internal.HiltCompilerOptions.isModuleInstallInCheckDisabled; 24 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 25 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 26 import static javax.lang.model.element.ElementKind.CLASS; 27 import static javax.lang.model.element.ElementKind.INTERFACE; 28 import static javax.lang.model.element.Modifier.ABSTRACT; 29 import static javax.lang.model.element.Modifier.PRIVATE; 30 import static javax.lang.model.element.Modifier.STATIC; 31 import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING; 32 33 import com.google.auto.service.AutoService; 34 import com.google.common.collect.ImmutableList; 35 import com.google.common.collect.ImmutableSet; 36 import com.squareup.javapoet.ClassName; 37 import dagger.hilt.processor.internal.BaseProcessor; 38 import dagger.hilt.processor.internal.ClassNames; 39 import dagger.hilt.processor.internal.Components; 40 import dagger.hilt.processor.internal.ProcessorErrors; 41 import dagger.hilt.processor.internal.Processors; 42 import java.util.HashSet; 43 import java.util.List; 44 import java.util.Optional; 45 import java.util.Set; 46 import javax.annotation.processing.Processor; 47 import javax.lang.model.element.AnnotationMirror; 48 import javax.lang.model.element.AnnotationValue; 49 import javax.lang.model.element.Element; 50 import javax.lang.model.element.ExecutableElement; 51 import javax.lang.model.element.Name; 52 import javax.lang.model.element.TypeElement; 53 import javax.lang.model.type.TypeKind; 54 import javax.lang.model.util.ElementFilter; 55 import javax.lang.model.util.SimpleAnnotationValueVisitor8; 56 import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; 57 58 /** Processor that outputs dummy files to propagate information through multiple javac runs. */ 59 @IncrementalAnnotationProcessor(ISOLATING) 60 @AutoService(Processor.class) 61 public final class AggregatedDepsProcessor extends BaseProcessor { 62 63 private static final ImmutableSet<ClassName> ENTRY_POINT_ANNOTATIONS = 64 ImmutableSet.of( 65 ClassNames.ENTRY_POINT, 66 ClassNames.EARLY_ENTRY_POINT, 67 ClassNames.GENERATED_ENTRY_POINT, 68 ClassNames.COMPONENT_ENTRY_POINT); 69 70 private static final ImmutableSet<ClassName> MODULE_ANNOTATIONS = 71 ImmutableSet.of( 72 ClassNames.MODULE); 73 74 private static final ImmutableSet<ClassName> INSTALL_IN_ANNOTATIONS = 75 ImmutableSet.of(ClassNames.INSTALL_IN, ClassNames.TEST_INSTALL_IN); 76 77 private final Set<Element> seen = new HashSet<>(); 78 79 @Override getSupportedAnnotationTypes()80 public Set<String> getSupportedAnnotationTypes() { 81 return ImmutableSet.builder() 82 .addAll(INSTALL_IN_ANNOTATIONS) 83 .addAll(MODULE_ANNOTATIONS) 84 .addAll(ENTRY_POINT_ANNOTATIONS) 85 .build() 86 .stream() 87 .map(Object::toString) 88 .collect(toImmutableSet()); 89 } 90 91 @Override processEach(TypeElement annotation, Element element)92 public void processEach(TypeElement annotation, Element element) throws Exception { 93 if (!seen.add(element)) { 94 return; 95 } 96 97 Optional<ClassName> installInAnnotation = getAnnotation(element, INSTALL_IN_ANNOTATIONS); 98 Optional<ClassName> entryPointAnnotation = getAnnotation(element, ENTRY_POINT_ANNOTATIONS); 99 Optional<ClassName> moduleAnnotation = getAnnotation(element, MODULE_ANNOTATIONS); 100 101 boolean hasInstallIn = installInAnnotation.isPresent(); 102 boolean isEntryPoint = entryPointAnnotation.isPresent(); 103 boolean isModule = moduleAnnotation.isPresent(); 104 105 ProcessorErrors.checkState( 106 !hasInstallIn || isEntryPoint || isModule, 107 element, 108 "@%s-annotated classes must also be annotated with @Module or @EntryPoint: %s", 109 installInAnnotation.map(ClassName::simpleName).orElse("@InstallIn"), 110 element); 111 112 ProcessorErrors.checkState( 113 !(isEntryPoint && isModule), 114 element, 115 "@%s and @%s cannot be used on the same interface: %s", 116 moduleAnnotation.map(ClassName::simpleName).orElse("@Module"), 117 entryPointAnnotation.map(ClassName::simpleName).orElse("@EntryPoint"), 118 element); 119 120 if (isModule) { 121 processModule(element, installInAnnotation, moduleAnnotation.get()); 122 } else if (isEntryPoint) { 123 processEntryPoint(element, installInAnnotation, entryPointAnnotation.get()); 124 } else { 125 throw new AssertionError(); 126 } 127 } 128 processModule( Element element, Optional<ClassName> installInAnnotation, ClassName moduleAnnotation)129 private void processModule( 130 Element element, Optional<ClassName> installInAnnotation, ClassName moduleAnnotation) 131 throws Exception { 132 ProcessorErrors.checkState( 133 installInAnnotation.isPresent() 134 || isDaggerGeneratedModule(element) 135 || installInCheckDisabled(element), 136 element, 137 "%s is missing an @InstallIn annotation. If this was intentional, see" 138 + " https://dagger.dev/hilt/compiler-options#disable-install-in-check for how to disable this" 139 + " check.", 140 element); 141 142 if (!installInAnnotation.isPresent()) { 143 // Modules without @InstallIn or @TestInstallIn annotations don't need to be processed further 144 return; 145 } 146 147 ProcessorErrors.checkState( 148 element.getKind() == CLASS || element.getKind() == INTERFACE, 149 element, 150 "Only classes and interfaces can be annotated with @Module: %s", 151 element); 152 TypeElement module = asType(element); 153 154 ProcessorErrors.checkState( 155 Processors.isTopLevel(module) 156 || module.getModifiers().contains(STATIC) 157 || module.getModifiers().contains(ABSTRACT) 158 || Processors.hasAnnotation(module.getEnclosingElement(), ClassNames.HILT_ANDROID_TEST), 159 module, 160 "Nested @%s modules must be static unless they are directly nested within a test. " 161 + "Found: %s", 162 installInAnnotation.get().simpleName(), 163 module); 164 165 // Check that if Dagger needs an instance of the module, Hilt can provide it automatically by 166 // calling a visible empty constructor. 167 ProcessorErrors.checkState( 168 // Skip ApplicationContextModule, since Hilt manages this module internally. 169 ClassNames.APPLICATION_CONTEXT_MODULE.equals(ClassName.get(module)) 170 || !Processors.requiresModuleInstance(getElementUtils(), module) 171 || hasVisibleEmptyConstructor(module), 172 module, 173 "Modules that need to be instantiated by Hilt must have a visible, empty constructor."); 174 175 // TODO(b/28989613): This should really be fixed in Dagger. Remove once Dagger bug is fixed. 176 ImmutableList<ExecutableElement> abstractMethodsWithMissingBinds = 177 ElementFilter.methodsIn(module.getEnclosedElements()).stream() 178 .filter(method -> method.getModifiers().contains(ABSTRACT)) 179 .filter(method -> !Processors.hasDaggerAbstractMethodAnnotation(method)) 180 .collect(toImmutableList()); 181 ProcessorErrors.checkState( 182 abstractMethodsWithMissingBinds.isEmpty(), 183 module, 184 "Found unimplemented abstract methods, %s, in an abstract module, %s. " 185 + "Did you forget to add a Dagger binding annotation (e.g. @Binds)?", 186 abstractMethodsWithMissingBinds, 187 module); 188 189 ImmutableList<TypeElement> replacedModules = ImmutableList.of(); 190 if (Processors.hasAnnotation(module, ClassNames.TEST_INSTALL_IN)) { 191 Optional<TypeElement> originatingTestElement = 192 Processors.getOriginatingTestElement(module, getElementUtils()); 193 ProcessorErrors.checkState( 194 !originatingTestElement.isPresent(), 195 // TODO(b/152801981): this should really error on the annotation value 196 module, 197 "@TestInstallIn modules cannot be nested in (or originate from) a " 198 + "@HiltAndroidTest-annotated class: %s", 199 originatingTestElement 200 .map(testElement -> testElement.getQualifiedName().toString()) 201 .orElse("")); 202 203 AnnotationMirror testInstallIn = 204 Processors.getAnnotationMirror(module, ClassNames.TEST_INSTALL_IN); 205 replacedModules = 206 Processors.getAnnotationClassValues(getElementUtils(), testInstallIn, "replaces"); 207 208 ProcessorErrors.checkState( 209 !replacedModules.isEmpty(), 210 // TODO(b/152801981): this should really error on the annotation value 211 module, 212 "@TestInstallIn#replaces() cannot be empty. Use @InstallIn instead."); 213 214 ImmutableList<TypeElement> nonInstallInModules = 215 replacedModules.stream() 216 .filter( 217 replacedModule -> 218 !Processors.hasAnnotation(replacedModule, ClassNames.INSTALL_IN)) 219 .collect(toImmutableList()); 220 221 ProcessorErrors.checkState( 222 nonInstallInModules.isEmpty(), 223 // TODO(b/152801981): this should really error on the annotation value 224 module, 225 "@TestInstallIn#replaces() can only contain @InstallIn modules, but found: %s", 226 nonInstallInModules); 227 228 ImmutableList<TypeElement> hiltWrapperModules = 229 replacedModules.stream() 230 .filter( 231 replacedModule -> 232 replacedModule.getSimpleName().toString().startsWith("HiltWrapper_")) 233 .collect(toImmutableList()); 234 235 ProcessorErrors.checkState( 236 hiltWrapperModules.isEmpty(), 237 // TODO(b/152801981): this should really error on the annotation value 238 module, 239 "@TestInstallIn#replaces() cannot contain Hilt generated public wrapper modules, " 240 + "but found: %s. ", 241 hiltWrapperModules); 242 243 if (!getPackage(module).getQualifiedName().toString().startsWith("dagger.hilt")) { 244 // Prevent external users from overriding Hilt's internal modules. Techincally, except for 245 // ApplicationContextModule, making all modules pkg-private should be enough but this is an 246 // extra measure of precaution. 247 ImmutableList<TypeElement> hiltInternalModules = 248 replacedModules.stream() 249 .filter( 250 replacedModule -> 251 getPackage(replacedModule) 252 .getQualifiedName() 253 .toString() 254 .startsWith("dagger.hilt")) 255 .collect(toImmutableList()); 256 257 ProcessorErrors.checkState( 258 hiltInternalModules.isEmpty(), 259 // TODO(b/152801981): this should really error on the annotation value 260 module, 261 "@TestInstallIn#replaces() cannot contain internal Hilt modules, but found: %s. ", 262 hiltInternalModules); 263 } 264 265 // Prevent users from uninstalling test-specific @InstallIn modules. 266 ImmutableList<TypeElement> replacedTestSpecificInstallIn = 267 replacedModules.stream() 268 .filter( 269 replacedModule -> 270 Processors.getOriginatingTestElement(replacedModule, getElementUtils()) 271 .isPresent()) 272 .collect(toImmutableList()); 273 274 ProcessorErrors.checkState( 275 replacedTestSpecificInstallIn.isEmpty(), 276 // TODO(b/152801981): this should really error on the annotation value 277 module, 278 "@TestInstallIn#replaces() cannot replace test specific @InstallIn modules, but found: " 279 + "%s. Please remove the @InstallIn module manually rather than replacing it.", 280 replacedTestSpecificInstallIn); 281 } 282 283 generateAggregatedDeps( 284 "modules", 285 module, 286 moduleAnnotation, 287 replacedModules.stream().map(ClassName::get).collect(toImmutableSet())); 288 } 289 processEntryPoint( Element element, Optional<ClassName> installInAnnotation, ClassName entryPointAnnotation)290 private void processEntryPoint( 291 Element element, Optional<ClassName> installInAnnotation, ClassName entryPointAnnotation) 292 throws Exception { 293 ProcessorErrors.checkState( 294 installInAnnotation.isPresent() , 295 element, 296 "@%s %s must also be annotated with @InstallIn", 297 entryPointAnnotation.simpleName(), 298 element); 299 300 ProcessorErrors.checkState( 301 !Processors.hasAnnotation(element, ClassNames.TEST_INSTALL_IN), 302 element, 303 "@TestInstallIn can only be used with modules"); 304 305 ProcessorErrors.checkState( 306 element.getKind() == INTERFACE, 307 element, 308 "Only interfaces can be annotated with @%s: %s", 309 entryPointAnnotation.simpleName(), 310 element); 311 TypeElement entryPoint = asType(element); 312 313 if (entryPointAnnotation.equals(ClassNames.EARLY_ENTRY_POINT)) { 314 ImmutableSet<ClassName> components = Components.getComponents(getElementUtils(), element); 315 ProcessorErrors.checkState( 316 components.equals(ImmutableSet.of(ClassNames.SINGLETON_COMPONENT)), 317 element, 318 "@EarlyEntryPoint can only be installed into the SingletonComponent. Found: %s", 319 components); 320 321 Optional<TypeElement> optionalTestElement = 322 Processors.getOriginatingTestElement(element, getElementUtils()); 323 ProcessorErrors.checkState( 324 !optionalTestElement.isPresent(), 325 element, 326 "@EarlyEntryPoint-annotated entry point, %s, cannot be nested in (or originate from) " 327 + "a @HiltAndroidTest-annotated class, %s. This requirement is to avoid confusion " 328 + "with other, test-specific entry points.", 329 asType(element).getQualifiedName().toString(), 330 optionalTestElement 331 .map(testElement -> testElement.getQualifiedName().toString()) 332 .orElse("")); 333 } 334 335 generateAggregatedDeps( 336 entryPointAnnotation.equals(ClassNames.COMPONENT_ENTRY_POINT) 337 ? "componentEntryPoints" 338 : "entryPoints", 339 entryPoint, 340 entryPointAnnotation, 341 ImmutableSet.of()); 342 } 343 generateAggregatedDeps( String key, TypeElement element, ClassName annotation, ImmutableSet<ClassName> replacedModules)344 private void generateAggregatedDeps( 345 String key, 346 TypeElement element, 347 ClassName annotation, 348 ImmutableSet<ClassName> replacedModules) 349 throws Exception { 350 // Get @InstallIn components here to catch errors before skipping user's pkg-private element. 351 ImmutableSet<ClassName> components = Components.getComponents(getElementUtils(), element); 352 353 if (isValidKind(element)) { 354 Optional<PkgPrivateMetadata> pkgPrivateMetadata = 355 PkgPrivateMetadata.of(getElementUtils(), element, annotation); 356 if (pkgPrivateMetadata.isPresent()) { 357 if (key.contentEquals("modules")) { 358 new PkgPrivateModuleGenerator(getProcessingEnv(), pkgPrivateMetadata.get()).generate(); 359 } else { 360 new PkgPrivateEntryPointGenerator(getProcessingEnv(), pkgPrivateMetadata.get()) 361 .generate(); 362 } 363 } else { 364 Optional<ClassName> testName = 365 Processors.getOriginatingTestElement(element, getElementUtils()).map(ClassName::get); 366 new AggregatedDepsGenerator( 367 key, element, testName, components, replacedModules, getProcessingEnv()) 368 .generate(); 369 } 370 } 371 } 372 getAnnotation( Element element, ImmutableSet<ClassName> annotations)373 private static Optional<ClassName> getAnnotation( 374 Element element, ImmutableSet<ClassName> annotations) { 375 ImmutableSet<ClassName> usedAnnotations = 376 annotations.stream() 377 .filter(annotation -> Processors.hasAnnotation(element, annotation)) 378 .collect(toImmutableSet()); 379 380 if (usedAnnotations.isEmpty()) { 381 return Optional.empty(); 382 } 383 384 ProcessorErrors.checkState( 385 usedAnnotations.size() == 1, 386 element, 387 "Only one of the following annotations can be used on %s: %s", 388 element, 389 usedAnnotations); 390 391 return Optional.of(getOnlyElement(usedAnnotations)); 392 } 393 isValidKind(Element element)394 private static boolean isValidKind(Element element) { 395 // don't go down the rabbit hole of analyzing undefined types. N.B. we don't issue 396 // an error here because javac already has and we don't want to spam the user. 397 return element.asType().getKind() != TypeKind.ERROR; 398 } 399 installInCheckDisabled(Element element)400 private boolean installInCheckDisabled(Element element) { 401 return isModuleInstallInCheckDisabled(getProcessingEnv()) 402 || Processors.hasAnnotation(element, ClassNames.DISABLE_INSTALL_IN_CHECK); 403 } 404 405 /** 406 * When using Dagger Producers, don't process generated modules. They will not have the expected 407 * annotations. 408 */ isDaggerGeneratedModule(Element element)409 private static boolean isDaggerGeneratedModule(Element element) { 410 if (!Processors.hasAnnotation(element, ClassNames.MODULE)) { 411 return false; 412 } 413 return element.getAnnotationMirrors().stream() 414 .filter(mirror -> isGenerated(mirror)) 415 .map(mirror -> asString(getOnlyElement(asList(getAnnotationValue(mirror, "value"))))) 416 .anyMatch(value -> value.startsWith("dagger")); 417 } 418 asList(AnnotationValue value)419 private static List<? extends AnnotationValue> asList(AnnotationValue value) { 420 return value.accept( 421 new SimpleAnnotationValueVisitor8<List<? extends AnnotationValue>, Void>() { 422 @Override 423 public List<? extends AnnotationValue> visitArray( 424 List<? extends AnnotationValue> value, Void unused) { 425 return value; 426 } 427 }, 428 null); 429 } 430 431 private static String asString(AnnotationValue value) { 432 return value.accept( 433 new SimpleAnnotationValueVisitor8<String, Void>() { 434 @Override 435 public String visitString(String value, Void unused) { 436 return value; 437 } 438 }, 439 null); 440 } 441 442 private static boolean isGenerated(AnnotationMirror annotationMirror) { 443 Name name = asType(annotationMirror.getAnnotationType().asElement()).getQualifiedName(); 444 return name.contentEquals("javax.annotation.Generated") 445 || name.contentEquals("javax.annotation.processing.Generated"); 446 } 447 448 private static boolean hasVisibleEmptyConstructor(TypeElement type) { 449 List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements()); 450 return constructors.isEmpty() 451 || constructors.stream() 452 .filter(constructor -> constructor.getParameters().isEmpty()) 453 .anyMatch( 454 constructor -> 455 !constructor.getModifiers().contains(PRIVATE) 456 ); 457 } 458 } 459