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