1 /* 2 * Copyright (C) 2020 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.uninstallmodules; 18 19 import static androidx.room.compiler.processing.XElementKt.isTypeElement; 20 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 21 22 import androidx.room.compiler.processing.XElement; 23 import androidx.room.compiler.processing.XProcessingEnv; 24 import androidx.room.compiler.processing.XTypeElement; 25 import com.google.common.collect.ImmutableList; 26 import com.google.common.collect.ImmutableSet; 27 import com.squareup.javapoet.ClassName; 28 import dagger.hilt.processor.internal.BaseProcessingStep; 29 import dagger.hilt.processor.internal.ClassNames; 30 import dagger.hilt.processor.internal.ProcessorErrors; 31 import dagger.hilt.processor.internal.Processors; 32 import dagger.internal.codegen.xprocessing.XAnnotations; 33 import dagger.internal.codegen.xprocessing.XElements; 34 35 /** Validates {@link dagger.hilt.android.testing.UninstallModules} usages. */ 36 public final class UninstallModulesProcessingStep extends BaseProcessingStep { 37 UninstallModulesProcessingStep(XProcessingEnv env)38 public UninstallModulesProcessingStep(XProcessingEnv env) { 39 super(env); 40 } 41 42 @Override annotationClassNames()43 protected ImmutableSet<ClassName> annotationClassNames() { 44 return ImmutableSet.of(ClassNames.UNINSTALL_MODULES); 45 } 46 47 @Override processEach(ClassName annotation, XElement element)48 public void processEach(ClassName annotation, XElement element) { 49 // TODO(bcorso): Consider using RootType to check this? 50 // TODO(bcorso): Loosen this restriction to allow defining sets of ignored modules in libraries. 51 ProcessorErrors.checkState( 52 isTypeElement(element) && element.hasAnnotation(ClassNames.HILT_ANDROID_TEST), 53 element, 54 "@%s should only be used on test classes annotated with @%s, but found: %s", 55 annotation.simpleName(), 56 ClassNames.HILT_ANDROID_TEST.simpleName(), 57 XElements.toStableString(element)); 58 59 XTypeElement testElement = XElements.asTypeElement(element); 60 ImmutableList<XTypeElement> uninstallModules = 61 XAnnotations.getAsTypeElementList( 62 testElement.getAnnotation(ClassNames.UNINSTALL_MODULES), "value"); 63 64 checkModulesHaveInstallIn(testElement, uninstallModules); 65 checkModulesDontOriginateFromTest(testElement, uninstallModules); 66 67 new AggregatedUninstallModulesGenerator(testElement, uninstallModules).generate(); 68 } 69 checkModulesHaveInstallIn( XTypeElement testElement, ImmutableList<XTypeElement> uninstallModules)70 private void checkModulesHaveInstallIn( 71 XTypeElement testElement, ImmutableList<XTypeElement> uninstallModules) { 72 ImmutableList<XTypeElement> invalidModules = 73 uninstallModules.stream() 74 .filter( 75 module -> 76 !(module.hasAnnotation(ClassNames.MODULE) 77 && module.hasAnnotation(ClassNames.INSTALL_IN))) 78 .collect(toImmutableList()); 79 80 ProcessorErrors.checkState( 81 invalidModules.isEmpty(), 82 // TODO(b/152801981): Point to the annotation value rather than the annotated element. 83 testElement, 84 "@UninstallModules should only include modules annotated with both @Module and @InstallIn, " 85 + "but found: %s.", 86 invalidModules.stream().map(XElements::toStableString).collect(toImmutableList())); 87 } 88 checkModulesDontOriginateFromTest( XTypeElement testElement, ImmutableList<XTypeElement> uninstallModules)89 private void checkModulesDontOriginateFromTest( 90 XTypeElement testElement, ImmutableList<XTypeElement> uninstallModules) { 91 ImmutableList<ClassName> invalidModules = 92 uninstallModules.stream() 93 .filter(module -> Processors.getOriginatingTestElement(module).isPresent()) 94 .map(XTypeElement::getClassName) 95 .collect(toImmutableList()); 96 97 ProcessorErrors.checkState( 98 invalidModules.isEmpty(), 99 // TODO(b/152801981): Point to the annotation value rather than the annotated element. 100 testElement, 101 "@UninstallModules should not contain test modules, but found: %s", 102 invalidModules); 103 } 104 } 105