• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.bindinggraphvalidation;
18 
19 import static com.google.common.collect.Sets.union;
20 import static dagger.internal.codegen.binding.ComponentRequirement.componentCanMakeNewInstances;
21 import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
22 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
23 import static java.util.stream.Collectors.joining;
24 import static javax.tools.Diagnostic.Kind.ERROR;
25 
26 import androidx.room.compiler.processing.XExecutableType;
27 import androidx.room.compiler.processing.XType;
28 import androidx.room.compiler.processing.XTypeElement;
29 import com.google.common.collect.ImmutableSet;
30 import com.google.common.collect.Sets;
31 import com.google.common.collect.Sets.SetView;
32 import dagger.internal.codegen.base.Util;
33 import dagger.internal.codegen.binding.ComponentNodeImpl;
34 import dagger.internal.codegen.model.BindingGraph;
35 import dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge;
36 import dagger.internal.codegen.model.BindingGraph.ComponentNode;
37 import dagger.internal.codegen.model.DiagnosticReporter;
38 import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
39 import java.util.HashMap;
40 import java.util.Map;
41 import java.util.Set;
42 import java.util.function.Function;
43 import javax.inject.Inject;
44 
45 /** Reports an error if a subcomponent factory method is missing required modules. */
46 final class SubcomponentFactoryMethodValidator extends ValidationBindingGraphPlugin {
47 
48   private final Map<ComponentNode, Set<XTypeElement>> inheritedModulesCache = new HashMap<>();
49 
50   @Inject
SubcomponentFactoryMethodValidator()51   SubcomponentFactoryMethodValidator() {}
52 
53   @Override
pluginName()54   public String pluginName() {
55     return "Dagger/SubcomponentFactoryMethodMissingModule";
56   }
57 
58   @Override
visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter)59   public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
60     if (!bindingGraph.rootComponentNode().isRealComponent()
61         || bindingGraph.rootComponentNode().isSubcomponent()) {
62       // We don't know all the modules that might be owned by the child until we know the real root
63       // component, which we don't if the root component node is really a module or a subcomponent.
64       return;
65     }
66     bindingGraph.network().edges().stream()
67         .flatMap(instancesOf(ChildFactoryMethodEdge.class))
68         .forEach(
69             edge -> {
70               ImmutableSet<XTypeElement> missingModules = findMissingModules(edge, bindingGraph);
71               if (!missingModules.isEmpty()) {
72                 reportMissingModuleParameters(
73                     edge, missingModules, bindingGraph, diagnosticReporter);
74               }
75             });
76   }
77 
findMissingModules( ChildFactoryMethodEdge edge, BindingGraph graph)78   private ImmutableSet<XTypeElement> findMissingModules(
79       ChildFactoryMethodEdge edge, BindingGraph graph) {
80     ImmutableSet<XTypeElement> factoryMethodParameters =
81         subgraphFactoryMethodParameters(edge, graph);
82     ComponentNode child = (ComponentNode) graph.network().incidentNodes(edge).target();
83     SetView<XTypeElement> modulesOwnedByChild = ownedModules(child, graph);
84     return graph.bindings().stream()
85         // bindings owned by child
86         .filter(binding -> binding.componentPath().equals(child.componentPath()))
87         // that require a module instance
88         .filter(binding -> binding.requiresModuleInstance())
89         .map(binding -> binding.contributingModule().get().xprocessing())
90         .distinct()
91         // module owned by child
92         .filter(module -> modulesOwnedByChild.contains(module))
93         // module not in the method parameters
94         .filter(module -> !factoryMethodParameters.contains(module))
95         // module doesn't have an accessible no-arg constructor
96         .filter(moduleType -> !componentCanMakeNewInstances(moduleType))
97         .collect(toImmutableSet());
98   }
99 
subgraphFactoryMethodParameters( ChildFactoryMethodEdge edge, BindingGraph bindingGraph)100   private ImmutableSet<XTypeElement> subgraphFactoryMethodParameters(
101       ChildFactoryMethodEdge edge, BindingGraph bindingGraph) {
102     ComponentNode parent = (ComponentNode) bindingGraph.network().incidentNodes(edge).source();
103     XType parentType = parent.componentPath().currentComponent().xprocessing().getType();
104     XExecutableType factoryMethodType = edge.factoryMethod().xprocessing().asMemberOf(parentType);
105     return factoryMethodType.getParameterTypes().stream()
106         .map(XType::getTypeElement)
107         .collect(toImmutableSet());
108   }
109 
ownedModules(ComponentNode component, BindingGraph graph)110   private SetView<XTypeElement> ownedModules(ComponentNode component, BindingGraph graph) {
111     return Sets.difference(
112         ((ComponentNodeImpl) component).componentDescriptor().moduleTypes(),
113         inheritedModules(component, graph));
114   }
115 
inheritedModules(ComponentNode component, BindingGraph graph)116   private Set<XTypeElement> inheritedModules(ComponentNode component, BindingGraph graph) {
117     return Util.reentrantComputeIfAbsent(
118         inheritedModulesCache, component, uncachedInheritedModules(graph));
119   }
120 
uncachedInheritedModules(BindingGraph graph)121   private Function<ComponentNode, Set<XTypeElement>> uncachedInheritedModules(BindingGraph graph) {
122     return componentNode ->
123         componentNode.componentPath().atRoot()
124             ? ImmutableSet.of()
125             : graph
126                 .componentNode(componentNode.componentPath().parent())
127                 .map(parent -> union(ownedModules(parent, graph), inheritedModules(parent, graph)))
128                 .get();
129   }
130 
reportMissingModuleParameters( ChildFactoryMethodEdge edge, ImmutableSet<XTypeElement> missingModules, BindingGraph graph, DiagnosticReporter diagnosticReporter)131   private void reportMissingModuleParameters(
132       ChildFactoryMethodEdge edge,
133       ImmutableSet<XTypeElement> missingModules,
134       BindingGraph graph,
135       DiagnosticReporter diagnosticReporter) {
136     diagnosticReporter.reportSubcomponentFactoryMethod(
137         ERROR,
138         edge,
139         "%s requires modules which have no visible default constructors. "
140             + "Add the following modules as parameters to this method: %s",
141         graph
142             .network()
143             .incidentNodes(edge)
144             .target()
145             .componentPath()
146             .currentComponent()
147             .className()
148             .canonicalName(),
149         missingModules.stream()
150             .map(XTypeElement::getQualifiedName)
151             .collect(joining(", ")));
152   }
153 }
154