All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dagger.internal.codegen.bindinggraphvalidation.SubcomponentFactoryMethodValidator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2018 The Dagger Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package dagger.internal.codegen.bindinggraphvalidation;

import static com.google.common.collect.Sets.union;
import static dagger.internal.codegen.binding.ComponentRequirement.componentCanMakeNewInstances;
import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static java.util.stream.Collectors.joining;
import static javax.tools.Diagnostic.Kind.ERROR;

import androidx.room.compiler.processing.XExecutableType;
import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import dagger.internal.codegen.base.Util;
import dagger.internal.codegen.binding.ComponentNodeImpl;
import dagger.internal.codegen.model.BindingGraph;
import dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge;
import dagger.internal.codegen.model.BindingGraph.ComponentNode;
import dagger.internal.codegen.model.DiagnosticReporter;
import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import javax.inject.Inject;

/** Reports an error if a subcomponent factory method is missing required modules. */
final class SubcomponentFactoryMethodValidator extends ValidationBindingGraphPlugin {

  private final Map> inheritedModulesCache = new HashMap<>();

  @Inject
  SubcomponentFactoryMethodValidator() {}

  @Override
  public String pluginName() {
    return "Dagger/SubcomponentFactoryMethodMissingModule";
  }

  @Override
  public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
    if (!bindingGraph.rootComponentNode().isRealComponent()
        || bindingGraph.rootComponentNode().isSubcomponent()) {
      // We don't know all the modules that might be owned by the child until we know the real root
      // component, which we don't if the root component node is really a module or a subcomponent.
      return;
    }
    bindingGraph.network().edges().stream()
        .flatMap(instancesOf(ChildFactoryMethodEdge.class))
        .forEach(
            edge -> {
              ImmutableSet missingModules = findMissingModules(edge, bindingGraph);
              if (!missingModules.isEmpty()) {
                reportMissingModuleParameters(
                    edge, missingModules, bindingGraph, diagnosticReporter);
              }
            });
  }

  private ImmutableSet findMissingModules(
      ChildFactoryMethodEdge edge, BindingGraph graph) {
    ImmutableSet factoryMethodParameters =
        subgraphFactoryMethodParameters(edge, graph);
    ComponentNode child = (ComponentNode) graph.network().incidentNodes(edge).target();
    SetView modulesOwnedByChild = ownedModules(child, graph);
    return graph.bindings().stream()
        // bindings owned by child
        .filter(binding -> binding.componentPath().equals(child.componentPath()))
        // that require a module instance
        .filter(binding -> binding.requiresModuleInstance())
        .map(binding -> binding.contributingModule().get().xprocessing())
        .distinct()
        // module owned by child
        .filter(module -> modulesOwnedByChild.contains(module))
        // module not in the method parameters
        .filter(module -> !factoryMethodParameters.contains(module))
        // module doesn't have an accessible no-arg constructor
        .filter(moduleType -> !componentCanMakeNewInstances(moduleType))
        .collect(toImmutableSet());
  }

  private ImmutableSet subgraphFactoryMethodParameters(
      ChildFactoryMethodEdge edge, BindingGraph bindingGraph) {
    ComponentNode parent = (ComponentNode) bindingGraph.network().incidentNodes(edge).source();
    XType parentType = parent.componentPath().currentComponent().xprocessing().getType();
    XExecutableType factoryMethodType = edge.factoryMethod().xprocessing().asMemberOf(parentType);
    return factoryMethodType.getParameterTypes().stream()
        .map(XType::getTypeElement)
        .collect(toImmutableSet());
  }

  private SetView ownedModules(ComponentNode component, BindingGraph graph) {
    return Sets.difference(
        ((ComponentNodeImpl) component).componentDescriptor().moduleTypes(),
        inheritedModules(component, graph));
  }

  private Set inheritedModules(ComponentNode component, BindingGraph graph) {
    return Util.reentrantComputeIfAbsent(
        inheritedModulesCache, component, uncachedInheritedModules(graph));
  }

  private Function> uncachedInheritedModules(BindingGraph graph) {
    return componentNode ->
        componentNode.componentPath().atRoot()
            ? ImmutableSet.of()
            : graph
                .componentNode(componentNode.componentPath().parent())
                .map(parent -> union(ownedModules(parent, graph), inheritedModules(parent, graph)))
                .get();
  }

  private void reportMissingModuleParameters(
      ChildFactoryMethodEdge edge,
      ImmutableSet missingModules,
      BindingGraph graph,
      DiagnosticReporter diagnosticReporter) {
    diagnosticReporter.reportSubcomponentFactoryMethod(
        ERROR,
        edge,
        "%s requires modules which have no visible default constructors. "
            + "Add the following modules as parameters to this method: %s",
        graph
            .network()
            .incidentNodes(edge)
            .target()
            .componentPath()
            .currentComponent()
            .className()
            .canonicalName(),
        missingModules.stream()
            .map(XTypeElement::getQualifiedName)
            .collect(joining(", ")));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy