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.getAnnotationValue; 20 import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotatedAnnotations; 21 import static java.util.stream.Collectors.toList; 22 import static javax.lang.model.element.Modifier.ABSTRACT; 23 24 import com.google.auto.common.MoreElements; 25 import com.google.auto.common.MoreTypes; 26 import com.google.auto.value.AutoValue; 27 import com.google.common.collect.ImmutableSet; 28 import com.google.common.collect.Sets; 29 import com.squareup.javapoet.AnnotationSpec; 30 import com.squareup.javapoet.ClassName; 31 import com.squareup.javapoet.TypeName; 32 import java.util.List; 33 import java.util.Optional; 34 import javax.annotation.processing.Messager; 35 import javax.lang.model.element.AnnotationMirror; 36 import javax.lang.model.element.AnnotationValue; 37 import javax.lang.model.element.Element; 38 import javax.lang.model.element.ExecutableElement; 39 import javax.lang.model.element.TypeElement; 40 import javax.lang.model.type.TypeMirror; 41 import javax.lang.model.util.SimpleAnnotationValueVisitor8; 42 import javax.tools.Diagnostic.Kind; 43 44 /** 45 * A descriptor of a generated {@link dagger.Module} and {@link dagger.Subcomponent} to be generated 46 * from a {@code ContributesAndroidInjector} method. 47 */ 48 @AutoValue 49 abstract class AndroidInjectorDescriptor { 50 /** The type to be injected; the return type of the {@code ContributesAndroidInjector} method. */ injectedType()51 abstract ClassName injectedType(); 52 53 /** Scopes to apply to the generated {@link dagger.Subcomponent}. */ scopes()54 abstract ImmutableSet<AnnotationSpec> scopes(); 55 56 /** See {@code ContributesAndroidInjector#modules()} */ modules()57 abstract ImmutableSet<ClassName> modules(); 58 59 /** The {@link dagger.Module} that contains the {@code ContributesAndroidInjector} method. */ enclosingModule()60 abstract ClassName enclosingModule(); 61 62 /** The method annotated with {@code ContributesAndroidInjector}. */ method()63 abstract ExecutableElement method(); 64 65 @AutoValue.Builder 66 abstract static class Builder { injectedType(ClassName injectedType)67 abstract Builder injectedType(ClassName injectedType); 68 scopesBuilder()69 abstract ImmutableSet.Builder<AnnotationSpec> scopesBuilder(); 70 modulesBuilder()71 abstract ImmutableSet.Builder<ClassName> modulesBuilder(); 72 enclosingModule(ClassName enclosingModule)73 abstract Builder enclosingModule(ClassName enclosingModule); 74 method(ExecutableElement method)75 abstract Builder method(ExecutableElement method); 76 build()77 abstract AndroidInjectorDescriptor build(); 78 } 79 80 static final class Validator { 81 private final Messager messager; 82 Validator(Messager messager)83 Validator(Messager messager) { 84 this.messager = messager; 85 } 86 87 /** 88 * Validates a {@code ContributesAndroidInjector} method, returning an {@link 89 * AndroidInjectorDescriptor} if it is valid, or {@link Optional#empty()} otherwise. 90 */ createIfValid(ExecutableElement method)91 Optional<AndroidInjectorDescriptor> createIfValid(ExecutableElement method) { 92 ErrorReporter reporter = new ErrorReporter(method, messager); 93 94 if (!method.getModifiers().contains(ABSTRACT)) { 95 reporter.reportError("@ContributesAndroidInjector methods must be abstract"); 96 } 97 98 if (!method.getParameters().isEmpty()) { 99 reporter.reportError("@ContributesAndroidInjector methods cannot have parameters"); 100 } 101 102 AndroidInjectorDescriptor.Builder builder = 103 new AutoValue_AndroidInjectorDescriptor.Builder().method(method); 104 TypeElement enclosingElement = MoreElements.asType(method.getEnclosingElement()); 105 if (!MoreDaggerElements.isAnnotationPresent(enclosingElement, TypeNames.MODULE)) { 106 reporter.reportError("@ContributesAndroidInjector methods must be in a @Module"); 107 } 108 builder.enclosingModule(ClassName.get(enclosingElement)); 109 110 TypeMirror injectedType = method.getReturnType(); 111 if (MoreTypes.asDeclared(injectedType).getTypeArguments().isEmpty()) { 112 builder.injectedType(ClassName.get(MoreTypes.asTypeElement(injectedType))); 113 } else { 114 reporter.reportError( 115 "@ContributesAndroidInjector methods cannot return parameterized types"); 116 } 117 118 AnnotationMirror annotation = 119 MoreDaggerElements.getAnnotationMirror(method, TypeNames.CONTRIBUTES_ANDROID_INJECTOR) 120 .get(); 121 for (TypeMirror module : 122 getAnnotationValue(annotation, "modules").accept(new AllTypesVisitor(), null)) { 123 if (MoreDaggerElements.isAnnotationPresent(MoreTypes.asElement(module), TypeNames.MODULE)) { 124 builder.modulesBuilder().add((ClassName) TypeName.get(module)); 125 } else { 126 reporter.reportError(String.format("%s is not a @Module", module), annotation); 127 } 128 } 129 130 for (AnnotationMirror scope : Sets.union( 131 getAnnotatedAnnotations(method, TypeNames.SCOPE), 132 getAnnotatedAnnotations(method, TypeNames.SCOPE_JAVAX))) { 133 builder.scopesBuilder().add(AnnotationSpec.get(scope)); 134 } 135 136 for (AnnotationMirror qualifier : Sets.union( 137 getAnnotatedAnnotations(method, TypeNames.QUALIFIER), 138 getAnnotatedAnnotations(method, TypeNames.QUALIFIER_JAVAX))) { 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