1 /* 2 * Copyright (C) 2014 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.internal.codegen.validation; 18 19 import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations; 20 import static com.google.auto.common.MoreElements.isAnnotationPresent; 21 import static com.google.auto.common.MoreTypes.asTypeElement; 22 import static com.google.auto.common.Visibility.PRIVATE; 23 import static com.google.auto.common.Visibility.PUBLIC; 24 import static com.google.auto.common.Visibility.effectiveVisibilityOfElement; 25 import static com.google.common.base.Preconditions.checkArgument; 26 import static com.google.common.collect.Iterables.getOnlyElement; 27 import static dagger.internal.codegen.base.ComponentAnnotation.componentAnnotation; 28 import static dagger.internal.codegen.base.ComponentAnnotation.isComponentAnnotation; 29 import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotation; 30 import static dagger.internal.codegen.base.ModuleAnnotation.isModuleAnnotation; 31 import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation; 32 import static dagger.internal.codegen.base.MoreAnnotationMirrors.simpleName; 33 import static dagger.internal.codegen.base.MoreAnnotationValues.asType; 34 import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent; 35 import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.getCreatorAnnotations; 36 import static dagger.internal.codegen.binding.ConfigurationAnnotations.getSubcomponentCreator; 37 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 38 import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror; 39 import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent; 40 import static java.util.stream.Collectors.joining; 41 import static javax.lang.model.element.Modifier.ABSTRACT; 42 import static javax.lang.model.element.Modifier.STATIC; 43 import static javax.lang.model.util.ElementFilter.methodsIn; 44 45 import com.google.auto.common.MoreElements; 46 import com.google.auto.common.MoreTypes; 47 import com.google.auto.common.Visibility; 48 import com.google.common.base.Joiner; 49 import com.google.common.collect.ImmutableList; 50 import com.google.common.collect.ImmutableListMultimap; 51 import com.google.common.collect.ImmutableSet; 52 import com.google.common.collect.ListMultimap; 53 import com.google.common.collect.MultimapBuilder; 54 import com.google.common.collect.Multimaps; 55 import com.google.common.collect.Sets; 56 import com.google.errorprone.annotations.FormatMethod; 57 import dagger.Module; 58 import dagger.Subcomponent; 59 import dagger.internal.codegen.base.ModuleAnnotation; 60 import dagger.internal.codegen.binding.BindingGraphFactory; 61 import dagger.internal.codegen.binding.ComponentCreatorAnnotation; 62 import dagger.internal.codegen.binding.ComponentDescriptorFactory; 63 import dagger.internal.codegen.binding.MethodSignatureFormatter; 64 import dagger.internal.codegen.binding.ModuleKind; 65 import dagger.internal.codegen.kotlin.KotlinMetadataUtil; 66 import dagger.internal.codegen.langmodel.DaggerElements; 67 import dagger.internal.codegen.langmodel.DaggerTypes; 68 import dagger.model.BindingGraph; 69 import dagger.producers.ProducerModule; 70 import dagger.producers.ProductionSubcomponent; 71 import java.lang.annotation.Annotation; 72 import java.util.ArrayList; 73 import java.util.Collection; 74 import java.util.EnumSet; 75 import java.util.HashMap; 76 import java.util.HashSet; 77 import java.util.List; 78 import java.util.Map; 79 import java.util.Map.Entry; 80 import java.util.Optional; 81 import java.util.Set; 82 import javax.inject.Inject; 83 import javax.inject.Scope; 84 import javax.inject.Singleton; 85 import javax.lang.model.element.AnnotationMirror; 86 import javax.lang.model.element.AnnotationValue; 87 import javax.lang.model.element.ElementKind; 88 import javax.lang.model.element.ExecutableElement; 89 import javax.lang.model.element.Name; 90 import javax.lang.model.element.TypeElement; 91 import javax.lang.model.type.DeclaredType; 92 import javax.lang.model.type.TypeMirror; 93 import javax.lang.model.util.SimpleAnnotationValueVisitor8; 94 import javax.lang.model.util.SimpleTypeVisitor8; 95 96 /** A {@linkplain ValidationReport validator} for {@link Module}s or {@link ProducerModule}s. */ 97 @Singleton 98 public final class ModuleValidator { 99 private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_TYPES = 100 ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class); 101 private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_CREATOR_TYPES = 102 ImmutableSet.of( 103 Subcomponent.Builder.class, 104 Subcomponent.Factory.class, 105 ProductionSubcomponent.Builder.class, 106 ProductionSubcomponent.Factory.class); 107 private static final Optional<Class<?>> ANDROID_PROCESSOR; 108 private static final String CONTRIBUTES_ANDROID_INJECTOR_NAME = 109 "dagger.android.ContributesAndroidInjector"; 110 private static final String ANDROID_PROCESSOR_NAME = "dagger.android.processor.AndroidProcessor"; 111 112 static { 113 Class<?> clazz; 114 try { 115 clazz = Class.forName(ANDROID_PROCESSOR_NAME, false, ModuleValidator.class.getClassLoader()); 116 } catch (ClassNotFoundException ignored) { 117 clazz = null; 118 } 119 ANDROID_PROCESSOR = Optional.ofNullable(clazz); 120 } 121 122 private final DaggerTypes types; 123 private final DaggerElements elements; 124 private final AnyBindingMethodValidator anyBindingMethodValidator; 125 private final MethodSignatureFormatter methodSignatureFormatter; 126 private final ComponentDescriptorFactory componentDescriptorFactory; 127 private final BindingGraphFactory bindingGraphFactory; 128 private final BindingGraphValidator bindingGraphValidator; 129 private final KotlinMetadataUtil metadataUtil; 130 private final Map<TypeElement, ValidationReport<TypeElement>> cache = new HashMap<>(); 131 private final Set<TypeElement> knownModules = new HashSet<>(); 132 133 @Inject ModuleValidator( DaggerTypes types, DaggerElements elements, AnyBindingMethodValidator anyBindingMethodValidator, MethodSignatureFormatter methodSignatureFormatter, ComponentDescriptorFactory componentDescriptorFactory, BindingGraphFactory bindingGraphFactory, BindingGraphValidator bindingGraphValidator, KotlinMetadataUtil metadataUtil)134 ModuleValidator( 135 DaggerTypes types, 136 DaggerElements elements, 137 AnyBindingMethodValidator anyBindingMethodValidator, 138 MethodSignatureFormatter methodSignatureFormatter, 139 ComponentDescriptorFactory componentDescriptorFactory, 140 BindingGraphFactory bindingGraphFactory, 141 BindingGraphValidator bindingGraphValidator, 142 KotlinMetadataUtil metadataUtil) { 143 this.types = types; 144 this.elements = elements; 145 this.anyBindingMethodValidator = anyBindingMethodValidator; 146 this.methodSignatureFormatter = methodSignatureFormatter; 147 this.componentDescriptorFactory = componentDescriptorFactory; 148 this.bindingGraphFactory = bindingGraphFactory; 149 this.bindingGraphValidator = bindingGraphValidator; 150 this.metadataUtil = metadataUtil; 151 } 152 153 /** 154 * Adds {@code modules} to the set of module types that will be validated during this compilation 155 * step. If a component or module includes a module that is not in this set, that included module 156 * is assumed to be valid because it was processed in a previous compilation step. If it were 157 * invalid, that previous compilation step would have failed and blocked this one. 158 * 159 * <p>This logic depends on this method being called before {@linkplain #validate(TypeElement) 160 * validating} any module or {@linkplain #validateReferencedModules(TypeElement, AnnotationMirror, 161 * ImmutableSet, Set) component}. 162 */ addKnownModules(Collection<TypeElement> modules)163 public void addKnownModules(Collection<TypeElement> modules) { 164 knownModules.addAll(modules); 165 } 166 167 /** Returns a validation report for a module type. */ validate(TypeElement module)168 public ValidationReport<TypeElement> validate(TypeElement module) { 169 return validate(module, new HashSet<>()); 170 } 171 validate( TypeElement module, Set<TypeElement> visitedModules)172 private ValidationReport<TypeElement> validate( 173 TypeElement module, Set<TypeElement> visitedModules) { 174 if (visitedModules.add(module)) { 175 return reentrantComputeIfAbsent(cache, module, m -> validateUncached(module, visitedModules)); 176 } 177 return ValidationReport.about(module).build(); 178 } 179 validateUncached( TypeElement module, Set<TypeElement> visitedModules)180 private ValidationReport<TypeElement> validateUncached( 181 TypeElement module, Set<TypeElement> visitedModules) { 182 ValidationReport.Builder<TypeElement> builder = ValidationReport.about(module); 183 ModuleKind moduleKind = ModuleKind.forAnnotatedElement(module).get(); 184 TypeElement contributesAndroidInjectorElement = 185 elements.getTypeElement(CONTRIBUTES_ANDROID_INJECTOR_NAME); 186 TypeMirror contributesAndroidInjector = 187 contributesAndroidInjectorElement != null 188 ? contributesAndroidInjectorElement.asType() 189 : null; 190 List<ExecutableElement> moduleMethods = methodsIn(module.getEnclosedElements()); 191 List<ExecutableElement> bindingMethods = new ArrayList<>(); 192 for (ExecutableElement moduleMethod : moduleMethods) { 193 if (anyBindingMethodValidator.isBindingMethod(moduleMethod)) { 194 builder.addSubreport(anyBindingMethodValidator.validate(moduleMethod)); 195 bindingMethods.add(moduleMethod); 196 } 197 198 for (AnnotationMirror annotation : moduleMethod.getAnnotationMirrors()) { 199 if (!ANDROID_PROCESSOR.isPresent() 200 && MoreTypes.equivalence() 201 .equivalent(contributesAndroidInjector, annotation.getAnnotationType())) { 202 builder.addSubreport( 203 ValidationReport.about(moduleMethod) 204 .addError( 205 String.format( 206 "@%s was used, but %s was not found on the processor path", 207 CONTRIBUTES_ANDROID_INJECTOR_NAME, ANDROID_PROCESSOR_NAME)) 208 .build()); 209 break; 210 } 211 } 212 } 213 214 if (bindingMethods.stream() 215 .map(ModuleMethodKind::ofMethod) 216 .collect(toImmutableSet()) 217 .containsAll( 218 EnumSet.of(ModuleMethodKind.ABSTRACT_DECLARATION, ModuleMethodKind.INSTANCE_BINDING))) { 219 builder.addError( 220 String.format( 221 "A @%s may not contain both non-static and abstract binding methods", 222 moduleKind.annotation().getSimpleName())); 223 } 224 225 validateModuleVisibility(module, moduleKind, builder); 226 227 ImmutableListMultimap<Name, ExecutableElement> bindingMethodsByName = 228 Multimaps.index(bindingMethods, ExecutableElement::getSimpleName); 229 230 validateMethodsWithSameName(builder, bindingMethodsByName); 231 if (module.getKind() != ElementKind.INTERFACE) { 232 validateBindingMethodOverrides( 233 module, 234 builder, 235 Multimaps.index(moduleMethods, ExecutableElement::getSimpleName), 236 bindingMethodsByName); 237 } 238 validateModifiers(module, builder); 239 validateReferencedModules(module, moduleKind, visitedModules, builder); 240 validateReferencedSubcomponents(module, moduleKind, builder); 241 validateNoScopeAnnotationsOnModuleElement(module, moduleKind, builder); 242 validateSelfCycles(module, builder); 243 if (metadataUtil.hasEnclosedCompanionObject(module)) { 244 validateCompanionModule(module, builder); 245 } 246 247 if (builder.build().isClean() 248 && bindingGraphValidator.shouldDoFullBindingGraphValidation(module)) { 249 validateModuleBindings(module, builder); 250 } 251 252 return builder.build(); 253 } 254 validateReferencedSubcomponents( final TypeElement subject, ModuleKind moduleKind, final ValidationReport.Builder<TypeElement> builder)255 private void validateReferencedSubcomponents( 256 final TypeElement subject, 257 ModuleKind moduleKind, 258 final ValidationReport.Builder<TypeElement> builder) { 259 // TODO(ronshapiro): use validateTypesAreDeclared when it is checked in 260 ModuleAnnotation moduleAnnotation = moduleAnnotation(moduleKind.getModuleAnnotation(subject)); 261 for (AnnotationValue subcomponentAttribute : 262 moduleAnnotation.subcomponentsAsAnnotationValues()) { 263 asType(subcomponentAttribute) 264 .accept( 265 new SimpleTypeVisitor8<Void, Void>() { 266 @Override 267 protected Void defaultAction(TypeMirror e, Void aVoid) { 268 builder.addError( 269 e + " is not a valid subcomponent type", 270 subject, 271 moduleAnnotation.annotation(), 272 subcomponentAttribute); 273 return null; 274 } 275 276 @Override 277 public Void visitDeclared(DeclaredType declaredType, Void aVoid) { 278 TypeElement attributeType = asTypeElement(declaredType); 279 if (isAnyAnnotationPresent(attributeType, SUBCOMPONENT_TYPES)) { 280 validateSubcomponentHasBuilder( 281 attributeType, moduleAnnotation.annotation(), builder); 282 } else { 283 builder.addError( 284 isAnyAnnotationPresent(attributeType, SUBCOMPONENT_CREATOR_TYPES) 285 ? moduleSubcomponentsIncludesCreator(attributeType) 286 : moduleSubcomponentsIncludesNonSubcomponent(attributeType), 287 subject, 288 moduleAnnotation.annotation(), 289 subcomponentAttribute); 290 } 291 292 return null; 293 } 294 }, 295 null); 296 } 297 } 298 moduleSubcomponentsIncludesNonSubcomponent(TypeElement notSubcomponent)299 private static String moduleSubcomponentsIncludesNonSubcomponent(TypeElement notSubcomponent) { 300 return notSubcomponent.getQualifiedName() 301 + " is not a @Subcomponent or @ProductionSubcomponent"; 302 } 303 moduleSubcomponentsIncludesCreator( TypeElement moduleSubcomponentsAttribute)304 private static String moduleSubcomponentsIncludesCreator( 305 TypeElement moduleSubcomponentsAttribute) { 306 TypeElement subcomponentType = 307 MoreElements.asType(moduleSubcomponentsAttribute.getEnclosingElement()); 308 ComponentCreatorAnnotation creatorAnnotation = 309 getOnlyElement(getCreatorAnnotations(moduleSubcomponentsAttribute)); 310 return String.format( 311 "%s is a @%s.%s. Did you mean to use %s?", 312 moduleSubcomponentsAttribute.getQualifiedName(), 313 subcomponentAnnotation(subcomponentType).get().simpleName(), 314 creatorAnnotation.creatorKind().typeName(), 315 subcomponentType.getQualifiedName()); 316 } 317 validateSubcomponentHasBuilder( TypeElement subcomponentAttribute, AnnotationMirror moduleAnnotation, ValidationReport.Builder<TypeElement> builder)318 private static void validateSubcomponentHasBuilder( 319 TypeElement subcomponentAttribute, 320 AnnotationMirror moduleAnnotation, 321 ValidationReport.Builder<TypeElement> builder) { 322 if (getSubcomponentCreator(subcomponentAttribute).isPresent()) { 323 return; 324 } 325 builder.addError( 326 moduleSubcomponentsDoesntHaveCreator(subcomponentAttribute, moduleAnnotation), 327 builder.getSubject(), 328 moduleAnnotation); 329 } 330 moduleSubcomponentsDoesntHaveCreator( TypeElement subcomponent, AnnotationMirror moduleAnnotation)331 private static String moduleSubcomponentsDoesntHaveCreator( 332 TypeElement subcomponent, AnnotationMirror moduleAnnotation) { 333 return String.format( 334 "%1$s doesn't have a @%2$s.Builder or @%2$s.Factory, which is required when used with " 335 + "@%3$s.subcomponents", 336 subcomponent.getQualifiedName(), 337 subcomponentAnnotation(subcomponent).get().simpleName(), 338 simpleName(moduleAnnotation)); 339 } 340 341 enum ModuleMethodKind { 342 ABSTRACT_DECLARATION, 343 INSTANCE_BINDING, 344 STATIC_BINDING, 345 ; 346 ofMethod(ExecutableElement moduleMethod)347 static ModuleMethodKind ofMethod(ExecutableElement moduleMethod) { 348 if (moduleMethod.getModifiers().contains(STATIC)) { 349 return STATIC_BINDING; 350 } else if (moduleMethod.getModifiers().contains(ABSTRACT)) { 351 return ABSTRACT_DECLARATION; 352 } else { 353 return INSTANCE_BINDING; 354 } 355 } 356 } 357 validateModifiers( TypeElement subject, ValidationReport.Builder<TypeElement> builder)358 private void validateModifiers( 359 TypeElement subject, ValidationReport.Builder<TypeElement> builder) { 360 // This coupled with the check for abstract modules in ComponentValidator guarantees that 361 // only modules without type parameters are referenced from @Component(modules={...}). 362 if (!subject.getTypeParameters().isEmpty() && !subject.getModifiers().contains(ABSTRACT)) { 363 builder.addError("Modules with type parameters must be abstract", subject); 364 } 365 } 366 validateMethodsWithSameName( ValidationReport.Builder<TypeElement> builder, ListMultimap<Name, ExecutableElement> bindingMethodsByName)367 private void validateMethodsWithSameName( 368 ValidationReport.Builder<TypeElement> builder, 369 ListMultimap<Name, ExecutableElement> bindingMethodsByName) { 370 for (Entry<Name, Collection<ExecutableElement>> entry : 371 bindingMethodsByName.asMap().entrySet()) { 372 if (entry.getValue().size() > 1) { 373 for (ExecutableElement offendingMethod : entry.getValue()) { 374 builder.addError( 375 String.format( 376 "Cannot have more than one binding method with the same name in a single module"), 377 offendingMethod); 378 } 379 } 380 } 381 } 382 validateReferencedModules( TypeElement subject, ModuleKind moduleKind, Set<TypeElement> visitedModules, ValidationReport.Builder<TypeElement> builder)383 private void validateReferencedModules( 384 TypeElement subject, 385 ModuleKind moduleKind, 386 Set<TypeElement> visitedModules, 387 ValidationReport.Builder<TypeElement> builder) { 388 // Validate that all the modules we include are valid for inclusion. 389 AnnotationMirror mirror = moduleKind.getModuleAnnotation(subject); 390 builder.addSubreport( 391 validateReferencedModules( 392 subject, mirror, moduleKind.legalIncludedModuleKinds(), visitedModules)); 393 } 394 395 /** 396 * Validates modules included in a given module or installed in a given component. 397 * 398 * <p>Checks that the referenced modules are non-generic types annotated with {@code @Module} or 399 * {@code @ProducerModule}. 400 * 401 * <p>If the referenced module is in the {@linkplain #addKnownModules(Collection) known modules 402 * set} and has errors, reports an error at that module's inclusion. 403 * 404 * @param annotatedType the annotated module or component 405 * @param annotation the annotation specifying the referenced modules ({@code @Component}, 406 * {@code @ProductionComponent}, {@code @Subcomponent}, {@code @ProductionSubcomponent}, 407 * {@code @Module}, or {@code @ProducerModule}) 408 * @param validModuleKinds the module kinds that the annotated type is permitted to include 409 */ validateReferencedModules( TypeElement annotatedType, AnnotationMirror annotation, ImmutableSet<ModuleKind> validModuleKinds, Set<TypeElement> visitedModules)410 ValidationReport<TypeElement> validateReferencedModules( 411 TypeElement annotatedType, 412 AnnotationMirror annotation, 413 ImmutableSet<ModuleKind> validModuleKinds, 414 Set<TypeElement> visitedModules) { 415 ValidationReport.Builder<TypeElement> subreport = ValidationReport.about(annotatedType); 416 ImmutableSet<? extends Class<? extends Annotation>> validModuleAnnotations = 417 validModuleKinds.stream().map(ModuleKind::annotation).collect(toImmutableSet()); 418 419 for (AnnotationValue includedModule : getModules(annotation)) { 420 asType(includedModule) 421 .accept( 422 new SimpleTypeVisitor8<Void, Void>() { 423 @Override 424 protected Void defaultAction(TypeMirror mirror, Void p) { 425 reportError("%s is not a valid module type.", mirror); 426 return null; 427 } 428 429 @Override 430 public Void visitDeclared(DeclaredType t, Void p) { 431 TypeElement module = MoreElements.asType(t.asElement()); 432 if (!t.getTypeArguments().isEmpty()) { 433 reportError( 434 "%s is listed as a module, but has type parameters", 435 module.getQualifiedName()); 436 } 437 if (!isAnyAnnotationPresent(module, validModuleAnnotations)) { 438 reportError( 439 "%s is listed as a module, but is not annotated with %s", 440 module.getQualifiedName(), 441 (validModuleAnnotations.size() > 1 ? "one of " : "") 442 + validModuleAnnotations.stream() 443 .map(otherClass -> "@" + otherClass.getSimpleName()) 444 .collect(joining(", "))); 445 } else if (knownModules.contains(module) 446 && !validate(module, visitedModules).isClean()) { 447 reportError("%s has errors", module.getQualifiedName()); 448 } 449 if (metadataUtil.isCompanionObjectClass(module)) { 450 reportError( 451 "%s is listed as a module, but it is a companion object class. " 452 + "Add @Module to the enclosing class and reference that instead.", 453 module.getQualifiedName()); 454 } 455 return null; 456 } 457 458 @FormatMethod 459 private void reportError(String format, Object... args) { 460 subreport.addError( 461 String.format(format, args), annotatedType, annotation, includedModule); 462 } 463 }, 464 null); 465 } 466 return subreport.build(); 467 } 468 getModules(AnnotationMirror annotation)469 private static ImmutableList<AnnotationValue> getModules(AnnotationMirror annotation) { 470 if (isModuleAnnotation(annotation)) { 471 return moduleAnnotation(annotation).includesAsAnnotationValues(); 472 } 473 if (isComponentAnnotation(annotation)) { 474 return componentAnnotation(annotation).moduleValues(); 475 } 476 throw new IllegalArgumentException(String.format("unsupported annotation: %s", annotation)); 477 } 478 validateBindingMethodOverrides( TypeElement subject, ValidationReport.Builder<TypeElement> builder, ImmutableListMultimap<Name, ExecutableElement> moduleMethodsByName, ImmutableListMultimap<Name, ExecutableElement> bindingMethodsByName)479 private void validateBindingMethodOverrides( 480 TypeElement subject, 481 ValidationReport.Builder<TypeElement> builder, 482 ImmutableListMultimap<Name, ExecutableElement> moduleMethodsByName, 483 ImmutableListMultimap<Name, ExecutableElement> bindingMethodsByName) { 484 // For every binding method, confirm it overrides nothing *and* nothing overrides it. 485 // Consider the following hierarchy: 486 // class Parent { 487 // @Provides Foo a() {} 488 // @Provides Foo b() {} 489 // Foo c() {} 490 // } 491 // class Child extends Parent { 492 // @Provides Foo a() {} 493 // Foo b() {} 494 // @Provides Foo c() {} 495 // } 496 // In each of those cases, we want to fail. "a" is clear, "b" because Child is overriding 497 // a binding method in Parent, and "c" because Child is defining a binding method that overrides 498 // Parent. 499 TypeElement currentClass = subject; 500 TypeMirror objectType = elements.getTypeElement(Object.class).asType(); 501 // We keep track of methods that failed so we don't spam with multiple failures. 502 Set<ExecutableElement> failedMethods = Sets.newHashSet(); 503 ListMultimap<Name, ExecutableElement> allMethodsByName = 504 MultimapBuilder.hashKeys().arrayListValues().build(moduleMethodsByName); 505 506 while (!types.isSameType(currentClass.getSuperclass(), objectType)) { 507 currentClass = MoreElements.asType(types.asElement(currentClass.getSuperclass())); 508 List<ExecutableElement> superclassMethods = methodsIn(currentClass.getEnclosedElements()); 509 for (ExecutableElement superclassMethod : superclassMethods) { 510 Name name = superclassMethod.getSimpleName(); 511 // For each method in the superclass, confirm our binding methods don't override it 512 for (ExecutableElement bindingMethod : bindingMethodsByName.get(name)) { 513 if (failedMethods.add(bindingMethod) 514 && elements.overrides(bindingMethod, superclassMethod, subject)) { 515 builder.addError( 516 String.format( 517 "Binding methods may not override another method. Overrides: %s", 518 methodSignatureFormatter.format(superclassMethod)), 519 bindingMethod); 520 } 521 } 522 // For each binding method in superclass, confirm our methods don't override it. 523 if (anyBindingMethodValidator.isBindingMethod(superclassMethod)) { 524 for (ExecutableElement method : allMethodsByName.get(name)) { 525 if (failedMethods.add(method) 526 && elements.overrides(method, superclassMethod, subject)) { 527 builder.addError( 528 String.format( 529 "Binding methods may not be overridden in modules. Overrides: %s", 530 methodSignatureFormatter.format(superclassMethod)), 531 method); 532 } 533 } 534 } 535 allMethodsByName.put(superclassMethod.getSimpleName(), superclassMethod); 536 } 537 } 538 } 539 validateModuleVisibility( final TypeElement moduleElement, ModuleKind moduleKind, final ValidationReport.Builder<?> reportBuilder)540 private void validateModuleVisibility( 541 final TypeElement moduleElement, 542 ModuleKind moduleKind, 543 final ValidationReport.Builder<?> reportBuilder) { 544 ModuleAnnotation moduleAnnotation = 545 moduleAnnotation(getAnnotationMirror(moduleElement, moduleKind.annotation()).get()); 546 Visibility moduleVisibility = Visibility.ofElement(moduleElement); 547 Visibility moduleEffectiveVisibility = effectiveVisibilityOfElement(moduleElement); 548 if (moduleVisibility.equals(PRIVATE)) { 549 reportBuilder.addError("Modules cannot be private.", moduleElement); 550 } else if (moduleEffectiveVisibility.equals(PRIVATE)) { 551 reportBuilder.addError("Modules cannot be enclosed in private types.", moduleElement); 552 } 553 554 switch (moduleElement.getNestingKind()) { 555 case ANONYMOUS: 556 throw new IllegalStateException("Can't apply @Module to an anonymous class"); 557 case LOCAL: 558 throw new IllegalStateException("Local classes shouldn't show up in the processor"); 559 case MEMBER: 560 case TOP_LEVEL: 561 if (moduleEffectiveVisibility.equals(PUBLIC)) { 562 ImmutableSet<TypeElement> invalidVisibilityIncludes = 563 getModuleIncludesWithInvalidVisibility(moduleAnnotation); 564 if (!invalidVisibilityIncludes.isEmpty()) { 565 reportBuilder.addError( 566 String.format( 567 "This module is public, but it includes non-public (or effectively non-public) " 568 + "modules (%s) that have non-static, non-abstract binding methods. Either " 569 + "reduce the visibility of this module, make the included modules " 570 + "public, or make all of the binding methods on the included modules " 571 + "abstract or static.", 572 formatListForErrorMessage(invalidVisibilityIncludes.asList())), 573 moduleElement); 574 } 575 } 576 } 577 } 578 getModuleIncludesWithInvalidVisibility( ModuleAnnotation moduleAnnotation)579 private ImmutableSet<TypeElement> getModuleIncludesWithInvalidVisibility( 580 ModuleAnnotation moduleAnnotation) { 581 return moduleAnnotation.includes().stream() 582 .filter(include -> !effectiveVisibilityOfElement(include).equals(PUBLIC)) 583 .filter(this::requiresModuleInstance) 584 .collect(toImmutableSet()); 585 } 586 587 /** 588 * Returns {@code true} if a module instance is needed for any of the binding methods on the given 589 * {@code module}. This is the case when the module has any binding methods that are neither 590 * {@code abstract} nor {@code static}. Alternatively, if the module is a Kotlin Object then the 591 * binding methods are considered {@code static}, requiring no module instance. 592 */ requiresModuleInstance(TypeElement module)593 private boolean requiresModuleInstance(TypeElement module) { 594 // Note elements.getAllMembers(module) rather than module.getEnclosedElements() here: we need to 595 // include binding methods declared in supertypes because unlike most other validations being 596 // done in this class, which assume that supertype binding methods will be validated in a 597 // separate call to the validator since the supertype itself must be a @Module, we need to look 598 // at all the binding methods in the module's type hierarchy here. 599 boolean isKotlinObject = 600 metadataUtil.isObjectClass(module) || metadataUtil.isCompanionObjectClass(module); 601 if (isKotlinObject) { 602 return false; 603 } 604 return methodsIn(elements.getAllMembers(module)).stream() 605 .filter(anyBindingMethodValidator::isBindingMethod) 606 .map(ExecutableElement::getModifiers) 607 .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC)); 608 } 609 validateNoScopeAnnotationsOnModuleElement( TypeElement module, ModuleKind moduleKind, ValidationReport.Builder<TypeElement> report)610 private void validateNoScopeAnnotationsOnModuleElement( 611 TypeElement module, ModuleKind moduleKind, ValidationReport.Builder<TypeElement> report) { 612 for (AnnotationMirror scope : getAnnotatedAnnotations(module, Scope.class)) { 613 report.addError( 614 String.format( 615 "@%ss cannot be scoped. Did you mean to scope a method instead?", 616 moduleKind.annotation().getSimpleName()), 617 module, 618 scope); 619 } 620 } 621 validateSelfCycles( TypeElement module, ValidationReport.Builder<TypeElement> builder)622 private void validateSelfCycles( 623 TypeElement module, ValidationReport.Builder<TypeElement> builder) { 624 ModuleAnnotation moduleAnnotation = moduleAnnotation(module).get(); 625 moduleAnnotation 626 .includesAsAnnotationValues() 627 .forEach( 628 value -> 629 value.accept( 630 new SimpleAnnotationValueVisitor8<Void, Void>() { 631 @Override 632 public Void visitType(TypeMirror includedModule, Void aVoid) { 633 if (MoreTypes.equivalence().equivalent(module.asType(), includedModule)) { 634 String moduleKind = moduleAnnotation.annotationName(); 635 builder.addError( 636 String.format("@%s cannot include themselves.", moduleKind), 637 module, 638 moduleAnnotation.annotation(), 639 value); 640 } 641 return null; 642 } 643 }, 644 null)); 645 } 646 validateCompanionModule( TypeElement module, ValidationReport.Builder<TypeElement> builder)647 private void validateCompanionModule( 648 TypeElement module, ValidationReport.Builder<TypeElement> builder) { 649 checkArgument(metadataUtil.hasEnclosedCompanionObject(module)); 650 TypeElement companionModule = metadataUtil.getEnclosedCompanionObject(module); 651 List<ExecutableElement> companionModuleMethods = 652 methodsIn(companionModule.getEnclosedElements()); 653 List<ExecutableElement> companionBindingMethods = new ArrayList<>(); 654 for (ExecutableElement companionModuleMethod : companionModuleMethods) { 655 if (anyBindingMethodValidator.isBindingMethod(companionModuleMethod)) { 656 builder.addSubreport(anyBindingMethodValidator.validate(companionModuleMethod)); 657 companionBindingMethods.add(companionModuleMethod); 658 } 659 660 // On normal modules only overriding other binding methods is disallowed, but for companion 661 // objects we are prohibiting any override. For this can rely on checking the @Override 662 // annotation since the Kotlin compiler will always produce them for overriding methods. 663 if (isAnnotationPresent(companionModuleMethod, Override.class)) { 664 builder.addError( 665 "Binding method in companion object may not override another method.", 666 companionModuleMethod); 667 } 668 669 // TODO(danysantiago): Be strict about the usage of @JvmStatic, i.e. tell user to remove it. 670 } 671 672 ImmutableListMultimap<Name, ExecutableElement> bindingMethodsByName = 673 Multimaps.index(companionBindingMethods, ExecutableElement::getSimpleName); 674 validateMethodsWithSameName(builder, bindingMethodsByName); 675 676 // If there are provision methods, then check the visibility. Companion objects are composed by 677 // an inner class and a static field, it is not enough to check the visibility on the type 678 // element or the field, therefore we check the metadata. 679 if (!companionBindingMethods.isEmpty() && metadataUtil.isVisibilityPrivate(companionModule)) { 680 builder.addError( 681 "A Companion Module with binding methods cannot be private.", companionModule); 682 } 683 } 684 validateModuleBindings( TypeElement module, ValidationReport.Builder<TypeElement> report)685 private void validateModuleBindings( 686 TypeElement module, ValidationReport.Builder<TypeElement> report) { 687 BindingGraph bindingGraph = 688 bindingGraphFactory.create( 689 componentDescriptorFactory.moduleComponentDescriptor(module), true) 690 .topLevelBindingGraph(); 691 if (!bindingGraphValidator.isValid(bindingGraph)) { 692 // Since the validator uses a DiagnosticReporter to report errors, the ValdiationReport won't 693 // have any Items for them. We have to tell the ValidationReport that some errors were 694 // reported for the subject. 695 report.markDirty(); 696 } 697 } 698 formatListForErrorMessage(List<?> things)699 private static String formatListForErrorMessage(List<?> things) { 700 switch (things.size()) { 701 case 0: 702 return ""; 703 case 1: 704 return things.get(0).toString(); 705 default: 706 StringBuilder output = new StringBuilder(); 707 Joiner.on(", ").appendTo(output, things.subList(0, things.size() - 1)); 708 output.append(" and ").append(things.get(things.size() - 1)); 709 return output.toString(); 710 } 711 } 712 } 713