1 /* 2 * Copyright (C) 2017 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.android.processor; 18 19 import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations; 20 import static com.google.auto.common.AnnotationMirrors.getAnnotationValue; 21 import static com.google.auto.common.MoreElements.getAnnotationMirror; 22 import static com.google.auto.common.MoreElements.isAnnotationPresent; 23 import static java.util.stream.Collectors.toList; 24 import static javax.lang.model.element.Modifier.ABSTRACT; 25 26 import com.google.auto.common.MoreElements; 27 import com.google.auto.common.MoreTypes; 28 import com.google.auto.value.AutoValue; 29 import com.google.common.collect.ImmutableSet; 30 import com.squareup.javapoet.AnnotationSpec; 31 import com.squareup.javapoet.ClassName; 32 import com.squareup.javapoet.TypeName; 33 import dagger.Module; 34 import dagger.android.ContributesAndroidInjector; 35 import java.util.List; 36 import java.util.Optional; 37 import javax.annotation.processing.Messager; 38 import javax.inject.Qualifier; 39 import javax.inject.Scope; 40 import javax.lang.model.element.AnnotationMirror; 41 import javax.lang.model.element.AnnotationValue; 42 import javax.lang.model.element.Element; 43 import javax.lang.model.element.ExecutableElement; 44 import javax.lang.model.element.TypeElement; 45 import javax.lang.model.type.TypeMirror; 46 import javax.lang.model.util.SimpleAnnotationValueVisitor8; 47 import javax.tools.Diagnostic.Kind; 48 49 /** 50 * A descriptor of a generated {@link Module} and {@link dagger.Subcomponent} to be generated from a 51 * {@link ContributesAndroidInjector} method. 52 */ 53 @AutoValue 54 abstract class AndroidInjectorDescriptor { 55 /** The type to be injected; the return type of the {@link ContributesAndroidInjector} method. */ injectedType()56 abstract ClassName injectedType(); 57 58 /** Scopes to apply to the generated {@link dagger.Subcomponent}. */ scopes()59 abstract ImmutableSet<AnnotationSpec> scopes(); 60 61 /** @see ContributesAndroidInjector#modules() */ modules()62 abstract ImmutableSet<ClassName> modules(); 63 64 /** The {@link Module} that contains the {@link ContributesAndroidInjector} method. */ enclosingModule()65 abstract ClassName enclosingModule(); 66 67 /** The method annotated with {@link ContributesAndroidInjector}. */ method()68 abstract ExecutableElement method(); 69 70 @AutoValue.Builder 71 abstract static class Builder { injectedType(ClassName injectedType)72 abstract Builder injectedType(ClassName injectedType); 73 scopesBuilder()74 abstract ImmutableSet.Builder<AnnotationSpec> scopesBuilder(); 75 modulesBuilder()76 abstract ImmutableSet.Builder<ClassName> modulesBuilder(); 77 enclosingModule(ClassName enclosingModule)78 abstract Builder enclosingModule(ClassName enclosingModule); 79 method(ExecutableElement method)80 abstract Builder method(ExecutableElement method); 81 build()82 abstract AndroidInjectorDescriptor build(); 83 } 84 85 static final class Validator { 86 private final Messager messager; 87 Validator(Messager messager)88 Validator(Messager messager) { 89 this.messager = messager; 90 } 91 92 /** 93 * Validates a {@link ContributesAndroidInjector} method, returning an {@link 94 * AndroidInjectorDescriptor} if it is valid, or {@link Optional#empty()} otherwise. 95 */ createIfValid(ExecutableElement method)96 Optional<AndroidInjectorDescriptor> createIfValid(ExecutableElement method) { 97 ErrorReporter reporter = new ErrorReporter(method, messager); 98 99 if (!method.getModifiers().contains(ABSTRACT)) { 100 reporter.reportError("@ContributesAndroidInjector methods must be abstract"); 101 } 102 103 if (!method.getParameters().isEmpty()) { 104 reporter.reportError("@ContributesAndroidInjector methods cannot have parameters"); 105 } 106 107 AndroidInjectorDescriptor.Builder builder = 108 new AutoValue_AndroidInjectorDescriptor.Builder().method(method); 109 TypeElement enclosingElement = MoreElements.asType(method.getEnclosingElement()); 110 if (!isAnnotationPresent(enclosingElement, Module.class)) { 111 reporter.reportError("@ContributesAndroidInjector methods must be in a @Module"); 112 } 113 builder.enclosingModule(ClassName.get(enclosingElement)); 114 115 TypeMirror injectedType = method.getReturnType(); 116 if (MoreTypes.asDeclared(injectedType).getTypeArguments().isEmpty()) { 117 builder.injectedType(ClassName.get(MoreTypes.asTypeElement(injectedType))); 118 } else { 119 reporter.reportError( 120 "@ContributesAndroidInjector methods cannot return parameterized types"); 121 } 122 123 AnnotationMirror annotation = 124 getAnnotationMirror(method, ContributesAndroidInjector.class).get(); 125 for (TypeMirror module : 126 getAnnotationValue(annotation, "modules").accept(new AllTypesVisitor(), null)) { 127 if (isAnnotationPresent(MoreTypes.asElement(module), Module.class)) { 128 builder.modulesBuilder().add((ClassName) TypeName.get(module)); 129 } else { 130 reporter.reportError(String.format("%s is not a @Module", module), annotation); 131 } 132 } 133 134 for (AnnotationMirror scope : getAnnotatedAnnotations(method, Scope.class)) { 135 builder.scopesBuilder().add(AnnotationSpec.get(scope)); 136 } 137 138 for (AnnotationMirror qualifier : getAnnotatedAnnotations(method, Qualifier.class)) { 139 reporter.reportError( 140 "@ContributesAndroidInjector methods cannot have qualifiers", qualifier); 141 } 142 143 return reporter.hasError ? Optional.empty() : Optional.of(builder.build()); 144 } 145 146 // TODO(ronshapiro): use ValidationReport once it is moved out of the compiler 147 private static class ErrorReporter { 148 private final Element subject; 149 private final Messager messager; 150 private boolean hasError; 151 ErrorReporter(Element subject, Messager messager)152 ErrorReporter(Element subject, Messager messager) { 153 this.subject = subject; 154 this.messager = messager; 155 } 156 reportError(String error)157 void reportError(String error) { 158 hasError = true; 159 messager.printMessage(Kind.ERROR, error, subject); 160 } 161 reportError(String error, AnnotationMirror annotation)162 void reportError(String error, AnnotationMirror annotation) { 163 hasError = true; 164 messager.printMessage(Kind.ERROR, error, subject, annotation); 165 } 166 } 167 } 168 169 private static final class AllTypesVisitor 170 extends SimpleAnnotationValueVisitor8<ImmutableSet<TypeMirror>, Void> { 171 @Override visitArray(List<? extends AnnotationValue> values, Void aVoid)172 public ImmutableSet<TypeMirror> visitArray(List<? extends AnnotationValue> values, Void aVoid) { 173 return ImmutableSet.copyOf( 174 values.stream().flatMap(v -> v.accept(this, null).stream()).collect(toList())); 175 } 176 177 @Override visitType(TypeMirror a, Void aVoid)178 public ImmutableSet<TypeMirror> visitType(TypeMirror a, Void aVoid) { 179 return ImmutableSet.of(a); 180 } 181 182 @Override defaultAction(Object o, Void aVoid)183 protected ImmutableSet<TypeMirror> defaultAction(Object o, Void aVoid) { 184 throw new AssertionError(o); 185 } 186 } 187 } 188