• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ComponentValidator(Elements elements, Types types, ModuleValidator moduleValidator, BuilderValidator subcomponentBuilderValidator)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 
ComponentValidator(Elements elements, Types types, ModuleValidator moduleValidator, ComponentValidator subcomponentValidator, BuilderValidator subcomponentBuilderValidator)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 
createForComponent(Elements elements, Types types, ModuleValidator moduleValidator, ComponentValidator subcomponentValidator, BuilderValidator subcomponentBuilderValidator)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 
createForSubcomponent(Elements elements, Types types, ModuleValidator moduleValidator, BuilderValidator subcomponentBuilderValidator)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 {
referencedSubcomponents()120     abstract Set<Element> referencedSubcomponents();
report()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    */
validate(final TypeElement subject, Set<? extends Element> validatedSubcomponents, Set<? extends Element> validatedSubcomponentBuilders)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 
validateSubcomponentMethod(final ValidationReport.Builder<TypeElement> builder, ExecutableElement method, List<? extends VariableElement> parameters, List<? extends TypeMirror> parameterTypes, TypeMirror returnType, Optional<AnnotationMirror> subcomponentAnnotation)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 
validateSubcomponentBuilderMethod(ValidationReport.Builder<TypeElement> builder, ExecutableElement method, List<? extends VariableElement> parameters, TypeMirror returnType, Set<? extends Element> validatedSubcomponentBuilders)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 
checkForAnnotation(TypeMirror type, final Class<? extends Annotation> annotation)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