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