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