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