Skip to content

Instantly share code, notes, and snippets.

@twocity
Last active August 29, 2015 14:22
Show Gist options
  • Save twocity/fbe5a60f78ca6219d004 to your computer and use it in GitHub Desktop.
Save twocity/fbe5a60f78ca6219d004 to your computer and use it in GitHub Desktop.
validateDependencyScopes
/**
   * Validates that among the dependencies are at most one scoped dependency,
   * that there are no cycles within the scoping chain, and that singleton
   * components have no scoped dependencies.
   */
  private void validateDependencyScopes(BindingGraph subject,
      Builder<BindingGraph> reportBuilder) {
    ComponentDescriptor descriptor = subject.componentDescriptor();
    Optional<AnnotationMirror> scope = subject.componentDescriptor().scope();
    ImmutableSet<TypeElement> scopedDependencies = scopedTypesIn(descriptor.dependencies());
    if (scope.isPresent()) {
      // Dagger 1.x scope compatibility requires this be suppress-able.
      if (scopeCycleValidationType.diagnosticKind().isPresent()
          && isTypeOf(Singleton.class, scope.get().getAnnotationType())) {
        // Singleton is a special-case representing the longest lifetime, and therefore
        // @Singleton components may not depend on scoped components
        if (!scopedDependencies.isEmpty()) {
          StringBuilder message = new StringBuilder(
              "This @Singleton component cannot depend on scoped components:\n");
          appendIndentedComponentsList(message, scopedDependencies);
          reportBuilder.addItem(message.toString(),
              scopeCycleValidationType.diagnosticKind().get(),
              descriptor.componentDefinitionType(),
              descriptor.componentAnnotation());
        }
      } else if (scopedDependencies.size() > 1) {
        // Scoped components may depend on at most one scoped component.
        StringBuilder message = new StringBuilder(ErrorMessages.format(scope.get()))
            .append(' ')
            .append(descriptor.componentDefinitionType().getQualifiedName())
            .append(" depends on more than one scoped component:\n");
        appendIndentedComponentsList(message, scopedDependencies);
        reportBuilder.addItem(message.toString(),
            descriptor.componentDefinitionType(),
            descriptor.componentAnnotation());
      } else {
        // Dagger 1.x scope compatibility requires this be suppress-able.
        if (!scopeCycleValidationType.equals(ValidationType.NONE)) {
          validateScopeHierarchy(descriptor.componentDefinitionType(),
              descriptor.componentDefinitionType(),
              reportBuilder,
              new ArrayDeque<Equivalence.Wrapper<AnnotationMirror>>(),
              new ArrayDeque<TypeElement>());
        }
      }
    } else {
      // Scopeless components may not depend on scoped components.
      if (!scopedDependencies.isEmpty()) {
        StringBuilder message =
            new StringBuilder(descriptor.componentDefinitionType().getQualifiedName())
                .append(" (unscoped) cannot depend on scoped components:\n");
        appendIndentedComponentsList(message, scopedDependencies);
        reportBuilder.addItem(message.toString(),
            descriptor.componentDefinitionType(),
            descriptor.componentAnnotation());
      }
    }
  }
   // For every @Provides method, confirm it overrides nothing *and* nothing overrides it.
    // Consider the following hierarchy:
    // class Parent {
    //    @Provides Foo a() {}
    //    @Provides Foo b() {}
    //    Foo c() {}
    // }
    // class Child extends Parent {
    //    @Provides Foo a() {}
    //    Foo b() {}
    //    @Provides Foo c() {}
    // }
    // In each of those cases, we want to fail.  "a" is clear, "b" because Child is overriding
    // a method marked @Provides in Parent, and "c" because Child is defining an @Provides
    // method that overrides Parent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment