Home | History | Annotate | Download | only in codegen
      1 /*
      2  * Copyright (C) 2014 Google, Inc.
      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 dagger.internal.codegen;
     17 
     18 import com.google.auto.common.MoreElements;
     19 import com.google.auto.common.MoreTypes;
     20 import com.google.auto.value.AutoValue;
     21 import com.google.common.base.Optional;
     22 import com.google.common.collect.ImmutableList;
     23 import com.google.common.collect.ImmutableSet;
     24 import com.google.common.collect.Iterables;
     25 import com.google.common.collect.LinkedHashMultimap;
     26 import com.google.common.collect.Multimap;
     27 import com.google.common.collect.Sets;
     28 import dagger.Component;
     29 import dagger.Module;
     30 import dagger.Subcomponent;
     31 import java.lang.annotation.Annotation;
     32 import java.util.Collection;
     33 import java.util.List;
     34 import java.util.Map;
     35 import java.util.Set;
     36 import javax.lang.model.element.AnnotationMirror;
     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.element.VariableElement;
     41 import javax.lang.model.type.DeclaredType;
     42 import javax.lang.model.type.ExecutableType;
     43 import javax.lang.model.type.TypeMirror;
     44 import javax.lang.model.util.ElementFilter;
     45 import javax.lang.model.util.Elements;
     46 import javax.lang.model.util.SimpleTypeVisitor6;
     47 import javax.lang.model.util.Types;
     48 
     49 import static com.google.auto.common.MoreElements.getAnnotationMirror;
     50 import static dagger.internal.codegen.ConfigurationAnnotations.enclosedBuilders;
     51 import static dagger.internal.codegen.ConfigurationAnnotations.getComponentModules;
     52 import static dagger.internal.codegen.ConfigurationAnnotations.getTransitiveModules;
     53 import static javax.lang.model.element.ElementKind.CLASS;
     54 import static javax.lang.model.element.ElementKind.INTERFACE;
     55 import static javax.lang.model.element.Modifier.ABSTRACT;
     56 import static javax.lang.model.type.TypeKind.VOID;
     57 
     58 /**
     59  * Performs superficial validation of the contract of the {@link Component} annotation.
     60  *
     61  * @author Gregory Kick
     62  */
     63 final class ComponentValidator {
     64   private final Elements elements;
     65   private final Types types;
     66   private final ComponentDescriptor.Kind componentType;
     67   private final ModuleValidator moduleValidator;
     68   private final ComponentValidator subcomponentValidator;
     69   private final BuilderValidator subcomponentBuilderValidator;
     70 
     71   private ComponentValidator(Elements elements,
     72       Types types,
     73       ModuleValidator moduleValidator,
     74       BuilderValidator subcomponentBuilderValidator) {
     75     this.elements = elements;
     76     this.types = types;
     77     this.componentType = ComponentDescriptor.Kind.SUBCOMPONENT;
     78     this.moduleValidator = moduleValidator;
     79     this.subcomponentValidator = this;
     80     this.subcomponentBuilderValidator = subcomponentBuilderValidator;
     81   }
     82 
     83   private ComponentValidator(Elements elements,
     84       Types types,
     85       ModuleValidator moduleValidator,
     86       ComponentValidator subcomponentValidator,
     87       BuilderValidator subcomponentBuilderValidator) {
     88     this.elements = elements;
     89     this.types = types;
     90     this.componentType = ComponentDescriptor.Kind.COMPONENT;
     91     this.moduleValidator = moduleValidator;
     92     this.subcomponentValidator = subcomponentValidator;
     93     this.subcomponentBuilderValidator = subcomponentBuilderValidator;
     94   }
     95 
     96   static ComponentValidator createForComponent(Elements elements,
     97       Types types,
     98       ModuleValidator moduleValidator,
     99       ComponentValidator subcomponentValidator,
    100       BuilderValidator subcomponentBuilderValidator) {
    101     return new ComponentValidator(elements,
    102         types,
    103         moduleValidator,
    104         subcomponentValidator,
    105         subcomponentBuilderValidator);
    106   }
    107 
    108   static ComponentValidator createForSubcomponent(Elements elements,
    109       Types types,
    110       ModuleValidator moduleValidator,
    111       BuilderValidator subcomponentBuilderValidator) {
    112     return new ComponentValidator(elements,
    113         types,
    114         moduleValidator,
    115         subcomponentBuilderValidator);
    116   }
    117 
    118   @AutoValue
    119   static abstract class ComponentValidationReport {
    120     abstract Set<Element> referencedSubcomponents();
    121     abstract ValidationReport<TypeElement> report();
    122   }
    123 
    124   /**
    125    * Validates the given component subject. Also validates any referenced subcomponents that aren't
    126    * already included in the {@code validatedSubcomponents} set.
    127    */
    128   public ComponentValidationReport validate(final TypeElement subject,
    129       Set<? extends Element> validatedSubcomponents,
    130       Set<? extends Element> validatedSubcomponentBuilders) {
    131     ValidationReport.Builder<TypeElement> builder = ValidationReport.about(subject);
    132 
    133     if (!subject.getKind().equals(INTERFACE)
    134         && !(subject.getKind().equals(CLASS) && subject.getModifiers().contains(ABSTRACT))) {
    135       builder.addError(
    136           String.format(
    137               "@%s may only be applied to an interface or abstract class",
    138               componentType.annotationType().getSimpleName()),
    139           subject);
    140     }
    141 
    142     ImmutableList<DeclaredType> builders =
    143         enclosedBuilders(subject, componentType.builderAnnotationType());
    144     if (builders.size() > 1) {
    145       builder.addError(
    146           String.format(ErrorMessages.builderMsgsFor(componentType).moreThanOne(), builders),
    147           subject);
    148     }
    149 
    150     DeclaredType subjectType = MoreTypes.asDeclared(subject.asType());
    151 
    152     // TODO(gak): This should use Util.findLocalAndInheritedMethods, otherwise
    153     // it can return a logical method multiple times (including overrides, etc.)
    154     List<? extends Element> members = elements.getAllMembers(subject);
    155     Multimap<Element, ExecutableElement> referencedSubcomponents = LinkedHashMultimap.create();
    156     for (ExecutableElement method : ElementFilter.methodsIn(members)) {
    157       if (method.getModifiers().contains(ABSTRACT)) {
    158         ExecutableType resolvedMethod =
    159             MoreTypes.asExecutable(types.asMemberOf(subjectType, method));
    160         List<? extends TypeMirror> parameterTypes = resolvedMethod.getParameterTypes();
    161         List<? extends VariableElement> parameters = method.getParameters();
    162         TypeMirror returnType = resolvedMethod.getReturnType();
    163 
    164         // abstract methods are ones we have to implement, so they each need to be validated
    165         // first, check the return type.  if it's a subcomponent, validate that method as such.
    166         Optional<AnnotationMirror> subcomponentAnnotation =
    167             checkForAnnotation(returnType, Subcomponent.class);
    168         Optional<AnnotationMirror> subcomponentBuilderAnnotation =
    169             checkForAnnotation(returnType, Subcomponent.Builder.class);
    170         if (subcomponentAnnotation.isPresent()) {
    171           referencedSubcomponents.put(MoreTypes.asElement(returnType), method);
    172           validateSubcomponentMethod(builder,
    173               method,
    174               parameters,
    175               parameterTypes,
    176               returnType,
    177               subcomponentAnnotation);
    178         } else if (subcomponentBuilderAnnotation.isPresent()) {
    179           referencedSubcomponents.put(MoreTypes.asElement(returnType).getEnclosingElement(),
    180               method);
    181           validateSubcomponentBuilderMethod(builder,
    182               method,
    183               parameters,
    184               returnType,
    185               validatedSubcomponentBuilders);
    186         } else {
    187           // if it's not a subcomponent...
    188           switch (parameters.size()) {
    189             case 0:
    190               // no parameters means that it is a provision method
    191               // basically, there are no restrictions here.  \o/
    192               break;
    193             case 1:
    194               // one parameter means that it's a members injection method
    195               TypeMirror onlyParameter = Iterables.getOnlyElement(parameterTypes);
    196               if (!(returnType.getKind().equals(VOID)
    197                   || types.isSameType(returnType, onlyParameter))) {
    198                 builder.addError(
    199                     "Members injection methods may only return the injected type or void.", method);
    200               }
    201               break;
    202             default:
    203               // this isn't any method that we know how to implement...
    204               builder.addError(
    205                   "This method isn't a valid provision method, members injection method or "
    206                       + "subcomponent factory method. Dagger cannot implement this method",
    207                   method);
    208               break;
    209           }
    210         }
    211       }
    212     }
    213 
    214     for (Map.Entry<Element, Collection<ExecutableElement>> entry :
    215         referencedSubcomponents.asMap().entrySet()) {
    216       if (entry.getValue().size() > 1) {
    217         builder.addError(
    218             String.format(
    219                 ErrorMessages.SubcomponentBuilderMessages.INSTANCE.moreThanOneRefToSubcomponent(),
    220                 entry.getKey(),
    221                 entry.getValue()),
    222             subject);
    223       }
    224     }
    225 
    226     AnnotationMirror componentMirror =
    227         getAnnotationMirror(subject, componentType.annotationType()).get();
    228     ImmutableList<TypeMirror> moduleTypes = getComponentModules(componentMirror);
    229     moduleValidator.validateReferencedModules(subject, builder, moduleTypes);
    230 
    231     // Make sure we validate any subcomponents we're referencing, unless we know we validated
    232     // them already in this pass.
    233     // TODO(sameb): If subcomponents refer to each other and both aren't in
    234     //              'validatedSubcomponents' (e.g, both aren't compiled in this pass),
    235     //              then this can loop forever.
    236     ImmutableSet.Builder<Element> allSubcomponents =
    237         ImmutableSet.<Element>builder().addAll(referencedSubcomponents.keySet());
    238     for (Element subcomponent :
    239         Sets.difference(referencedSubcomponents.keySet(), validatedSubcomponents)) {
    240       ComponentValidationReport subreport = subcomponentValidator.validate(
    241           MoreElements.asType(subcomponent), validatedSubcomponents, validatedSubcomponentBuilders);
    242       builder.addItems(subreport.report().items());
    243       allSubcomponents.addAll(subreport.referencedSubcomponents());
    244     }
    245 
    246     return new AutoValue_ComponentValidator_ComponentValidationReport(allSubcomponents.build(),
    247         builder.build());
    248   }
    249 
    250   private void validateSubcomponentMethod(final ValidationReport.Builder<TypeElement> builder,
    251       ExecutableElement method,
    252       List<? extends VariableElement> parameters,
    253       List<? extends TypeMirror> parameterTypes,
    254       TypeMirror returnType,
    255       Optional<AnnotationMirror> subcomponentAnnotation) {
    256     ImmutableSet<TypeElement> moduleTypes =
    257         MoreTypes.asTypeElements(getComponentModules(subcomponentAnnotation.get()));
    258 
    259     // TODO(gak): This logic maybe/probably shouldn't live here as it requires us to traverse
    260     // subcomponents and their modules separately from how it is done in ComponentDescriptor and
    261     // ModuleDescriptor
    262     @SuppressWarnings("deprecation")
    263     ImmutableSet<TypeElement> transitiveModules =
    264         getTransitiveModules(types, elements, moduleTypes);
    265 
    266     Set<TypeElement> variableTypes = Sets.newHashSet();
    267 
    268     for (int i = 0; i < parameterTypes.size(); i++) {
    269       VariableElement parameter = parameters.get(i);
    270       TypeMirror parameterType = parameterTypes.get(i);
    271       Optional<TypeElement> moduleType = parameterType.accept(
    272           new SimpleTypeVisitor6<Optional<TypeElement>, Void>() {
    273             @Override protected Optional<TypeElement> defaultAction(TypeMirror e, Void p) {
    274               return Optional.absent();
    275             }
    276 
    277             @Override public Optional<TypeElement> visitDeclared(DeclaredType t, Void p) {
    278               return MoreElements.isAnnotationPresent(t.asElement(), Module.class)
    279                   ? Optional.of(MoreTypes.asTypeElement(t))
    280                   : Optional.<TypeElement>absent();
    281             }
    282           }, null);
    283       if (moduleType.isPresent()) {
    284         if (variableTypes.contains(moduleType.get())) {
    285           builder.addError(
    286               String.format(
    287                   "A module may only occur once an an argument in a Subcomponent factory "
    288                       + "method, but %s was already passed.",
    289                   moduleType.get().getQualifiedName()),
    290               parameter);
    291         }
    292         if (!transitiveModules.contains(moduleType.get())) {
    293           builder.addError(
    294               String.format(
    295                   "%s is present as an argument to the %s factory method, but is not one of the"
    296                       + " modules used to implement the subcomponent.",
    297                   moduleType.get().getQualifiedName(),
    298                   MoreTypes.asTypeElement(returnType).getQualifiedName()),
    299               method);
    300         }
    301         variableTypes.add(moduleType.get());
    302       } else {
    303         builder.addError(
    304             String.format(
    305                 "Subcomponent factory methods may only accept modules, but %s is not.",
    306                 parameterType),
    307             parameter);
    308       }
    309     }
    310   }
    311 
    312   private void validateSubcomponentBuilderMethod(ValidationReport.Builder<TypeElement> builder,
    313       ExecutableElement method, List<? extends VariableElement> parameters, TypeMirror returnType,
    314       Set<? extends Element> validatedSubcomponentBuilders) {
    315 
    316     if (!parameters.isEmpty()) {
    317       builder.addError(
    318           ErrorMessages.SubcomponentBuilderMessages.INSTANCE.builderMethodRequiresNoArgs(), method);
    319     }
    320 
    321     // If we haven't already validated the subcomponent builder itself, validate it now.
    322     TypeElement builderElement = MoreTypes.asTypeElement(returnType);
    323     if (!validatedSubcomponentBuilders.contains(builderElement)) {
    324       // TODO(sameb): The builder validator right now assumes the element is being compiled
    325       // in this pass, which isn't true here.  We should change error messages to spit out
    326       // this method as the subject and add the original subject to the message output.
    327       builder.addItems(subcomponentBuilderValidator.validate(builderElement).items());
    328     }
    329   }
    330 
    331   private Optional<AnnotationMirror> checkForAnnotation(TypeMirror type,
    332       final Class<? extends Annotation> annotation) {
    333     return type.accept(new SimpleTypeVisitor6<Optional<AnnotationMirror>, Void>() {
    334       @Override
    335       protected Optional<AnnotationMirror> defaultAction(TypeMirror e, Void p) {
    336         return Optional.absent();
    337       }
    338 
    339       @Override
    340       public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void p) {
    341         return MoreElements.getAnnotationMirror(t.asElement(), annotation);
    342       }
    343     }, null);
    344   }
    345 }
    346